Delete deploy, restart, health and start/stop scripts for other services
Odin does not deploy, restart or health-probe anything: Idunn owns
deployment and daemon survival, and every service in this set is either
Idunn-deployed from its own repo recipe or archived. Removed from
scripts/: deploy-yggdrasil-{heimdall,repixelizer,source-app,streampixels}
(.ps1/.cmd, armed behind IDUNN_ACTUATOR=1) and the linux heimdall deploy;
restart-* (odin, stonks, vili, weksa, nightwing eve dashboard and browser
reference; several already exit 2 or throw as archived); health-* (odin,
voidbot, stonks, vili, weksa, mimir, nightwing, raven capture mux,
yggdrasil heimdall/repixelizer/streampixels/source-app; archived stubs and
orphans); start-odin.ps1 and stop-odin.ps1 (launched the deleted Node
coordinator).
Kept: write-gjallar-persona-cc.mjs (persona tooling, package.json
gjallar scripts) and the nightwing usb-move helpers, wasapi loopback,
firewall and wireguard scripts, none of which is a lifecycle actuator.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
06282c47d8
commit
092ff997b4
39 changed files with 0 additions and 1898 deletions
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File %~dp0deploy-yggdrasil-heimdall.ps1
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
param(
|
||||
[string] $SshTarget = "yggdrasil",
|
||||
[string] $RepoRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "Heimdall"),
|
||||
[string] $CultLibRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "CultLib"),
|
||||
[string] $AppUser = "heimdall",
|
||||
[string] $RemoteAppHome = "/srv/heimdall",
|
||||
[string] $RemoteTarballName = "heimdall-source.tar",
|
||||
[string] $RemoteCultLibTarballName = "heimdall-cultlib.tar",
|
||||
[string] $DeployScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\deploy-heimdall.sh"),
|
||||
[string] $CheckScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\check-heimdall.sh"),
|
||||
[string] $UpstreamRemote = "origin",
|
||||
[string] $UpstreamBranch = "main",
|
||||
[string] $CultLibUpstreamRemote = "origin",
|
||||
[string] $CultLibUpstreamBranch = "main"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
||||
throw "This deployment script is an Idunn actuator. Agents must configure Idunn release targets and let Idunn run deployment; do not invoke deploy scripts manually."
|
||||
}
|
||||
|
||||
foreach ($path in @($RepoRoot, $CultLibRoot, $DeployScript, $CheckScript)) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "Required path not found: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
|
||||
$cultLibSourceRef = "$CultLibUpstreamRemote/$CultLibUpstreamBranch"
|
||||
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $RepoRoot $sourceRef" }
|
||||
git -C $CultLibRoot fetch --prune $CultLibUpstreamRemote $CultLibUpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $CultLibRoot $cultLibSourceRef" }
|
||||
|
||||
$heimdallCommit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($heimdallCommit)) {
|
||||
throw "Could not determine Heimdall git revision."
|
||||
}
|
||||
$cultLibCommit = (git -C $CultLibRoot rev-parse $cultLibSourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($cultLibCommit)) {
|
||||
throw "Could not determine CultLib git revision."
|
||||
}
|
||||
|
||||
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") "yggdrasil-heimdall"
|
||||
$sourceTarPath = Join-Path $scratch $RemoteTarballName
|
||||
$cultLibTarPath = Join-Path $scratch $RemoteCultLibTarballName
|
||||
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
|
||||
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
|
||||
|
||||
git -C $RepoRoot archive --format=tar --output=$sourceTarPath $sourceRef
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git archive failed for $RepoRoot $sourceRef"
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $cultLibTarPath) {
|
||||
Remove-Item -LiteralPath $cultLibTarPath -Force
|
||||
}
|
||||
git -C $CultLibRoot archive --format=tar --output=$cultLibTarPath $cultLibSourceRef packages/cultnet-ts packages/cultcache-ts
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "CultLib tar build failed for $CultLibRoot $cultLibSourceRef"
|
||||
}
|
||||
|
||||
$sourceHash = (Get-FileHash $sourceTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$cultLibHash = (Get-FileHash $cultLibTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
|
||||
@(
|
||||
"schema=gamecult.idunn.deployment_manifest.v1"
|
||||
"appId=yggdrasil-heimdall"
|
||||
"repoRoot=$RepoRoot"
|
||||
"upstreamRemote=$UpstreamRemote"
|
||||
"upstreamBranch=$UpstreamBranch"
|
||||
"sourceRef=$sourceRef"
|
||||
"gitCommit=$heimdallCommit"
|
||||
"artifact=$RemoteTarballName"
|
||||
"sha256=$sourceHash"
|
||||
"cultLibRepoRoot=$CultLibRoot"
|
||||
"cultLibUpstreamRemote=$CultLibUpstreamRemote"
|
||||
"cultLibUpstreamBranch=$CultLibUpstreamBranch"
|
||||
"cultLibSourceRef=$cultLibSourceRef"
|
||||
"cultLibGitCommit=$cultLibCommit"
|
||||
"cultLibArtifact=$RemoteCultLibTarballName"
|
||||
"cultLibSha256=$cultLibHash"
|
||||
"deployedAtUtc=$deployedAt"
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
|
||||
|
||||
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$remoteSourceTarball = "/home/gamecultadmin/$RemoteTarballName"
|
||||
$remoteCultLibTarball = "/home/gamecultadmin/$RemoteCultLibTarballName"
|
||||
$remoteManifest = "/home/gamecultadmin/yggdrasil-heimdall-deployment-manifest.txt"
|
||||
$remoteRunner = "/home/gamecultadmin/run-yggdrasil-heimdall-deploy.sh"
|
||||
$targetSourceTarball = "$RemoteAppHome/$RemoteTarballName"
|
||||
$targetCultLibTarball = "$RemoteAppHome/$RemoteCultLibTarballName"
|
||||
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
|
||||
|
||||
$sftpBatchPath = Join-Path $scratch "yggdrasil-heimdall-upload.sftp"
|
||||
$runnerPath = Join-Path $scratch "run-yggdrasil-heimdall-deploy.sh"
|
||||
$runnerScript = @"
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteSourceTarball' '$targetSourceTarball'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteCultLibTarball' '$targetCultLibTarball'
|
||||
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
|
||||
chmod +x '$remoteDeploy' '$remoteCheck'
|
||||
sudo -n env GAMECULT_IDUNN_REMOTE_ACTUATOR=1 IDUNN_COMMAND_AUTHORITY=idunn-daemon bash '$remoteDeploy'
|
||||
|
||||
check_ok=0
|
||||
for i in `$(seq 1 30); do
|
||||
if sudo -n bash '$remoteCheck'; then
|
||||
check_ok=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "`$check_ok" -ne 1 ]; then
|
||||
sudo -n bash '$remoteCheck'
|
||||
exit 1
|
||||
fi
|
||||
"@.Replace("`r`n", "`n")
|
||||
[System.IO.File]::WriteAllText($runnerPath, $runnerScript, [System.Text.Encoding]::ASCII)
|
||||
@(
|
||||
"put ""$sourceTarPath"" ""$remoteSourceTarball"""
|
||||
"put ""$cultLibTarPath"" ""$remoteCultLibTarball"""
|
||||
"put ""$DeployScript"" ""$remoteDeploy"""
|
||||
"put ""$CheckScript"" ""$remoteCheck"""
|
||||
"put ""$manifestPath"" ""$remoteManifest"""
|
||||
"put ""$runnerPath"" ""$remoteRunner"""
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $sftpBatchPath
|
||||
|
||||
try {
|
||||
& sftp.exe -b $sftpBatchPath $SshTarget
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "sftp upload failed for yggdrasil-heimdall"
|
||||
}
|
||||
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "chmod +x '$remoteRunner' && bash '$remoteRunner'"
|
||||
$remoteExit = $LASTEXITCODE
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "rm -f '$remoteRunner'"
|
||||
if ($remoteExit -ne 0) {
|
||||
throw "remote deploy/check failed for yggdrasil-heimdall"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -LiteralPath $sftpBatchPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "yggdrasil-heimdall deployed with CultLib snapshot."
|
||||
Write-Host " heimdallCommit=$heimdallCommit"
|
||||
Write-Host " cultLibCommit=$cultLibCommit"
|
||||
Write-Host " artifactSha256=$sourceHash"
|
||||
Write-Host " cultLibSha256=$cultLibHash"
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File %~dp0deploy-yggdrasil-repixelizer.ps1
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
param(
|
||||
[string] $SshTarget = "yggdrasil",
|
||||
[string] $RepoRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "repixelizer"),
|
||||
[string] $CultLibRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "CultLib"),
|
||||
[string] $AppUser = "repixelizer",
|
||||
[string] $RemoteAppHome = "/srv/repixelizer",
|
||||
[string] $RemoteTarballName = "repixelizer-source.tar",
|
||||
[string] $RemoteCultLibTarballName = "repixelizer-cultlib.tar",
|
||||
[string] $DeployScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\deploy-repixelizer-gui.sh"),
|
||||
[string] $CheckScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\check-repixelizer-gui.sh"),
|
||||
[string] $UpstreamRemote = "origin",
|
||||
[string] $UpstreamBranch = "main",
|
||||
[string] $CultLibUpstreamRemote = "origin",
|
||||
[string] $CultLibUpstreamBranch = "main"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
||||
throw "This deployment script is an Idunn actuator. Agents must configure Idunn release targets and let Idunn run deployment; do not invoke deploy scripts manually."
|
||||
}
|
||||
|
||||
foreach ($path in @($RepoRoot, $CultLibRoot, $DeployScript, $CheckScript)) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "Required path not found: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
|
||||
$cultLibSourceRef = "$CultLibUpstreamRemote/$CultLibUpstreamBranch"
|
||||
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $RepoRoot $sourceRef" }
|
||||
git -C $CultLibRoot fetch --prune $CultLibUpstreamRemote $CultLibUpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $CultLibRoot $cultLibSourceRef" }
|
||||
|
||||
$repixelizerCommit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($repixelizerCommit)) {
|
||||
throw "Could not determine Repixelizer git revision."
|
||||
}
|
||||
|
||||
$cultLibCommit = (git -C $CultLibRoot rev-parse $cultLibSourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($cultLibCommit)) {
|
||||
throw "Could not determine CultLib git revision."
|
||||
}
|
||||
|
||||
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") "yggdrasil-repixelizer"
|
||||
$sourceTarPath = Join-Path $scratch $RemoteTarballName
|
||||
$cultLibTarPath = Join-Path $scratch $RemoteCultLibTarballName
|
||||
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
|
||||
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
|
||||
|
||||
git -C $RepoRoot archive --format=tar --output=$sourceTarPath $sourceRef
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git archive failed for $RepoRoot $sourceRef"
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $cultLibTarPath) {
|
||||
Remove-Item -LiteralPath $cultLibTarPath -Force
|
||||
}
|
||||
# All three Python packages. CultLib split packages/cultcache-py into
|
||||
# cultcache-py / cultnet-py / cultmesh-py; module names did not change, so
|
||||
# archiving only the first still succeeds and ships an incomplete library
|
||||
# that fails later as an ImportError, far from this line.
|
||||
git -C $CultLibRoot archive --format=tar --output=$cultLibTarPath $cultLibSourceRef packages/cultcache-py packages/cultnet-py packages/cultmesh-py
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "CultLib tar build failed for $CultLibRoot $cultLibSourceRef"
|
||||
}
|
||||
|
||||
$sourceHash = (Get-FileHash $sourceTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$cultLibHash = (Get-FileHash $cultLibTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
|
||||
@(
|
||||
"schema=gamecult.idunn.deployment_manifest.v1"
|
||||
"appId=yggdrasil-repixelizer"
|
||||
"repoRoot=$RepoRoot"
|
||||
"upstreamRemote=$UpstreamRemote"
|
||||
"upstreamBranch=$UpstreamBranch"
|
||||
"sourceRef=$sourceRef"
|
||||
"gitCommit=$repixelizerCommit"
|
||||
"artifact=$RemoteTarballName"
|
||||
"sha256=$sourceHash"
|
||||
"cultLibRepoRoot=$CultLibRoot"
|
||||
"cultLibUpstreamRemote=$CultLibUpstreamRemote"
|
||||
"cultLibUpstreamBranch=$CultLibUpstreamBranch"
|
||||
"cultLibSourceRef=$cultLibSourceRef"
|
||||
"cultLibGitCommit=$cultLibCommit"
|
||||
"cultLibArtifact=$RemoteCultLibTarballName"
|
||||
"cultLibSha256=$cultLibHash"
|
||||
"deployedAtUtc=$deployedAt"
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
|
||||
|
||||
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$remoteSourceTarball = "/home/gamecultadmin/$RemoteTarballName"
|
||||
$remoteCultLibTarball = "/home/gamecultadmin/$RemoteCultLibTarballName"
|
||||
$remoteManifest = "/home/gamecultadmin/yggdrasil-repixelizer-deployment-manifest.txt"
|
||||
$remoteRunner = "/home/gamecultadmin/run-yggdrasil-repixelizer-deploy.sh"
|
||||
$targetSourceTarball = "$RemoteAppHome/$RemoteTarballName"
|
||||
$targetCultLibTarball = "$RemoteAppHome/$RemoteCultLibTarballName"
|
||||
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
|
||||
|
||||
$sftpBatchPath = Join-Path $scratch "yggdrasil-repixelizer-upload.sftp"
|
||||
$runnerPath = Join-Path $scratch "run-yggdrasil-repixelizer-deploy.sh"
|
||||
$runnerScript = @"
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteSourceTarball' '$targetSourceTarball'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteCultLibTarball' '$targetCultLibTarball'
|
||||
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
|
||||
chmod +x '$remoteDeploy' '$remoteCheck'
|
||||
sudo -n env GAMECULT_IDUNN_REMOTE_ACTUATOR=1 IDUNN_COMMAND_AUTHORITY=idunn-daemon bash '$remoteDeploy'
|
||||
|
||||
check_ok=0
|
||||
for i in `$(seq 1 30); do
|
||||
if sudo -n bash '$remoteCheck'; then
|
||||
check_ok=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "`$check_ok" -ne 1 ]; then
|
||||
sudo -n bash '$remoteCheck'
|
||||
exit 1
|
||||
fi
|
||||
"@.Replace("`r`n", "`n")
|
||||
[System.IO.File]::WriteAllText($runnerPath, $runnerScript, [System.Text.Encoding]::ASCII)
|
||||
@(
|
||||
"put ""$sourceTarPath"" ""$remoteSourceTarball"""
|
||||
"put ""$cultLibTarPath"" ""$remoteCultLibTarball"""
|
||||
"put ""$DeployScript"" ""$remoteDeploy"""
|
||||
"put ""$CheckScript"" ""$remoteCheck"""
|
||||
"put ""$manifestPath"" ""$remoteManifest"""
|
||||
"put ""$runnerPath"" ""$remoteRunner"""
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $sftpBatchPath
|
||||
|
||||
try {
|
||||
& sftp.exe -b $sftpBatchPath $SshTarget
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "sftp upload failed for yggdrasil-repixelizer"
|
||||
}
|
||||
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "chmod +x '$remoteRunner' && bash '$remoteRunner'"
|
||||
$remoteExit = $LASTEXITCODE
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "rm -f '$remoteRunner'"
|
||||
if ($remoteExit -ne 0) {
|
||||
throw "remote deploy/check failed for yggdrasil-repixelizer"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -LiteralPath $sftpBatchPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "yggdrasil-repixelizer deployed with CultLib snapshot."
|
||||
Write-Host " repixelizerCommit=$repixelizerCommit"
|
||||
Write-Host " cultLibCommit=$cultLibCommit"
|
||||
Write-Host " artifactSha256=$sourceHash"
|
||||
Write-Host " cultLibSha256=$cultLibHash"
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)] [string] $AppId,
|
||||
[Parameter(Mandatory = $true)] [string] $RepoRoot,
|
||||
[Parameter(Mandatory = $true)] [string] $AppUser,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteAppHome,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteTarballName,
|
||||
[Parameter(Mandatory = $true)] [string] $DeployScript,
|
||||
[Parameter(Mandatory = $true)] [string] $CheckScript,
|
||||
[string] $SshTarget = "yggdrasil",
|
||||
[string] $UpstreamRemote = "origin",
|
||||
[string] $UpstreamBranch = "main",
|
||||
[string] $StateMigrationScript = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1") {
|
||||
throw "This deployment script is an Idunn actuator. Agents must configure Idunn release targets and let Idunn run deployment; do not invoke deploy scripts manually."
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $RepoRoot)) {
|
||||
throw "Repo root not found: $RepoRoot"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $DeployScript)) {
|
||||
throw "Deploy script not found: $DeployScript"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $CheckScript)) {
|
||||
throw "Check script not found: $CheckScript"
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($StateMigrationScript) -and -not (Test-Path -LiteralPath $StateMigrationScript)) {
|
||||
throw "State migration script not found: $StateMigrationScript"
|
||||
}
|
||||
|
||||
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git fetch failed for $RepoRoot $UpstreamRemote/$UpstreamBranch"
|
||||
}
|
||||
|
||||
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
|
||||
$commit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($commit)) {
|
||||
throw "Could not determine git revision for $RepoRoot $sourceRef"
|
||||
}
|
||||
|
||||
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") $AppId
|
||||
$tarPath = Join-Path $scratch $RemoteTarballName
|
||||
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
|
||||
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
|
||||
|
||||
git -C $RepoRoot archive --format=tar --output=$tarPath $sourceRef
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git archive failed for $RepoRoot $sourceRef"
|
||||
}
|
||||
|
||||
$hash = (Get-FileHash $tarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
|
||||
@(
|
||||
"schema=gamecult.idunn.deployment_manifest.v1"
|
||||
"appId=$AppId"
|
||||
"repoRoot=$RepoRoot"
|
||||
"upstreamRemote=$UpstreamRemote"
|
||||
"upstreamBranch=$UpstreamBranch"
|
||||
"sourceRef=$sourceRef"
|
||||
"gitCommit=$commit"
|
||||
"artifact=$RemoteTarballName"
|
||||
"sha256=$hash"
|
||||
"deployedAtUtc=$deployedAt"
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
|
||||
|
||||
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$remoteMigration = if ([string]::IsNullOrWhiteSpace($StateMigrationScript)) { "" } else { "/home/gamecultadmin/$([IO.Path]::GetFileName($StateMigrationScript))" }
|
||||
$remoteTarball = "/home/gamecultadmin/$RemoteTarballName"
|
||||
$remoteManifest = "/home/gamecultadmin/$AppId-deployment-manifest.txt"
|
||||
$remoteRunner = "/home/gamecultadmin/run-$AppId-deploy.sh"
|
||||
$targetTarball = "$RemoteAppHome/$RemoteTarballName"
|
||||
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
|
||||
$sshArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10", $SshTarget)
|
||||
$scpArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
|
||||
$runnerPath = Join-Path $scratch "run-$AppId-deploy.sh"
|
||||
|
||||
$runnerScript = @"
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteTarball' '$targetTarball'
|
||||
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
|
||||
chmod +x '$remoteDeploy' '$remoteCheck'
|
||||
if [ -n '$remoteMigration' ]; then
|
||||
chmod +x '$remoteMigration'
|
||||
sudo -n bash '$remoteMigration'
|
||||
fi
|
||||
sudo -n bash '$remoteDeploy'
|
||||
|
||||
check_ok=0
|
||||
for i in `$(seq 1 30); do
|
||||
if sudo -n bash '$remoteCheck'; then
|
||||
check_ok=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "`$check_ok" -ne 1 ]; then
|
||||
sudo -n bash '$remoteCheck'
|
||||
exit 1
|
||||
fi
|
||||
"@.Replace("`r`n", "`n")
|
||||
[System.IO.File]::WriteAllText($runnerPath, $runnerScript, [System.Text.Encoding]::ASCII)
|
||||
|
||||
scp.exe @scpArgs $tarPath "${SshTarget}:$remoteTarball"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp source tarball failed for $AppId" }
|
||||
scp.exe @scpArgs $DeployScript "${SshTarget}:$remoteDeploy"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp deploy script failed for $AppId" }
|
||||
scp.exe @scpArgs $CheckScript "${SshTarget}:$remoteCheck"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp check script failed for $AppId" }
|
||||
if (-not [string]::IsNullOrWhiteSpace($StateMigrationScript)) {
|
||||
scp.exe @scpArgs $StateMigrationScript "${SshTarget}:$remoteMigration"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp state migration script failed for $AppId" }
|
||||
}
|
||||
scp.exe @scpArgs $manifestPath "${SshTarget}:$remoteManifest"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp manifest failed for $AppId" }
|
||||
scp.exe @scpArgs $runnerPath "${SshTarget}:$remoteRunner"
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp runner script failed for $AppId" }
|
||||
|
||||
ssh.exe @sshArgs "chmod +x '$remoteRunner' && bash '$remoteRunner'"
|
||||
$remoteExit = $LASTEXITCODE
|
||||
ssh.exe @sshArgs "rm -f '$remoteRunner'"
|
||||
if ($remoteExit -ne 0) {
|
||||
throw "remote deploy/check failed for $AppId"
|
||||
}
|
||||
|
||||
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "$AppId deployed to Yggdrasil from $sourceRef at $commit."
|
||||
Write-Host " artifactSha256=$hash"
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo This deployment wrapper is an Idunn actuator. Configure Idunn; do not deploy manually.
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File %~dp0deploy-yggdrasil-streampixels.ps1
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
param(
|
||||
[string] $SshTarget = "yggdrasil",
|
||||
[string] $RepoRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "StreamPixels"),
|
||||
[string] $CultLibRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "CultLib"),
|
||||
[string] $AppUser = "streampixels",
|
||||
[string] $RemoteAppHome = "/srv/streampixels",
|
||||
[string] $RemoteTarballName = "streampixels-source.tar",
|
||||
[string] $RemoteCultLibTarballName = "streampixels-cultlib.tar",
|
||||
[string] $DeployScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\deploy-streampixels-preview.sh"),
|
||||
[string] $CheckScript = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "gamecult-ops\scripts\check-streampixels-preview.sh"),
|
||||
[string] $UpstreamRemote = "origin",
|
||||
[string] $UpstreamBranch = "main",
|
||||
[string] $CultLibUpstreamRemote = "origin",
|
||||
[string] $CultLibUpstreamBranch = "main"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
||||
throw "This deployment script is an Idunn actuator. Agents must configure Idunn release targets and let Idunn run deployment; do not invoke deploy scripts manually."
|
||||
}
|
||||
|
||||
foreach ($path in @($RepoRoot, $CultLibRoot, $DeployScript, $CheckScript)) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "Required path not found: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$sourceRef = "$UpstreamRemote/$UpstreamBranch"
|
||||
$cultLibSourceRef = "$CultLibUpstreamRemote/$CultLibUpstreamBranch"
|
||||
git -C $RepoRoot fetch --prune $UpstreamRemote $UpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $RepoRoot $sourceRef" }
|
||||
git -C $CultLibRoot fetch --prune $CultLibUpstreamRemote $CultLibUpstreamBranch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed for $CultLibRoot $cultLibSourceRef" }
|
||||
|
||||
$streamPixelsCommit = (git -C $RepoRoot rev-parse $sourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($streamPixelsCommit)) {
|
||||
throw "Could not determine StreamPixels git revision."
|
||||
}
|
||||
$cultLibCommit = (git -C $CultLibRoot rev-parse $cultLibSourceRef).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($cultLibCommit)) {
|
||||
throw "Could not determine CultLib git revision."
|
||||
}
|
||||
|
||||
$scratch = Join-Path (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\idunn-deploy") "yggdrasil-streampixels"
|
||||
$sourceTarPath = Join-Path $scratch $RemoteTarballName
|
||||
$cultLibTarPath = Join-Path $scratch $RemoteCultLibTarballName
|
||||
$manifestPath = Join-Path $scratch "deployment-manifest.txt"
|
||||
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
|
||||
|
||||
git -C $RepoRoot archive --format=tar --output=$sourceTarPath $sourceRef
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git archive failed for $RepoRoot $sourceRef"
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $cultLibTarPath) {
|
||||
Remove-Item -LiteralPath $cultLibTarPath -Force
|
||||
}
|
||||
git -C $CultLibRoot archive --format=tar --output=$cultLibTarPath $cultLibSourceRef package.json package-lock.json scripts/run-typescript-workspaces.mjs contracts/cultnet packages/cultcache-ts packages/cultnet-ts packages/cultmesh-ts packages/cultmesh-browser
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "CultLib tar build failed for $CultLibRoot $cultLibSourceRef"
|
||||
}
|
||||
|
||||
$sourceHash = (Get-FileHash $sourceTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$cultLibHash = (Get-FileHash $cultLibTarPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$deployedAt = [DateTimeOffset]::UtcNow.ToString("O")
|
||||
@(
|
||||
"schema=gamecult.idunn.deployment_manifest.v1"
|
||||
"appId=yggdrasil-streampixels"
|
||||
"repoRoot=$RepoRoot"
|
||||
"upstreamRemote=$UpstreamRemote"
|
||||
"upstreamBranch=$UpstreamBranch"
|
||||
"sourceRef=$sourceRef"
|
||||
"gitCommit=$streamPixelsCommit"
|
||||
"artifact=$RemoteTarballName"
|
||||
"sha256=$sourceHash"
|
||||
"cultLibRepoRoot=$CultLibRoot"
|
||||
"cultLibUpstreamRemote=$CultLibUpstreamRemote"
|
||||
"cultLibUpstreamBranch=$CultLibUpstreamBranch"
|
||||
"cultLibSourceRef=$cultLibSourceRef"
|
||||
"cultLibGitCommit=$cultLibCommit"
|
||||
"cultLibArtifact=$RemoteCultLibTarballName"
|
||||
"cultLibSha256=$cultLibHash"
|
||||
"deployedAtUtc=$deployedAt"
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $manifestPath
|
||||
|
||||
$remoteDeploy = "/home/gamecultadmin/$([IO.Path]::GetFileName($DeployScript))"
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$remoteSourceTarball = "/home/gamecultadmin/$RemoteTarballName"
|
||||
$remoteCultLibTarball = "/home/gamecultadmin/$RemoteCultLibTarballName"
|
||||
$remoteManifest = "/home/gamecultadmin/yggdrasil-streampixels-deployment-manifest.txt"
|
||||
$remoteRunner = "/home/gamecultadmin/run-yggdrasil-streampixels-deploy.sh"
|
||||
$targetSourceTarball = "$RemoteAppHome/$RemoteTarballName"
|
||||
$targetCultLibTarball = "$RemoteAppHome/$RemoteCultLibTarballName"
|
||||
$targetManifest = "$RemoteAppHome/deployment-manifest.txt"
|
||||
|
||||
$sftpBatchPath = Join-Path $scratch "yggdrasil-streampixels-upload.sftp"
|
||||
$runnerPath = Join-Path $scratch "run-yggdrasil-streampixels-deploy.sh"
|
||||
$runnerScript = @"
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sudo -n install -d -o '$AppUser' -g '$AppUser' '$RemoteAppHome'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteSourceTarball' '$targetSourceTarball'
|
||||
sudo -n install -o '$AppUser' -g '$AppUser' -m 600 '$remoteCultLibTarball' '$targetCultLibTarball'
|
||||
sudo -n install -m 644 '$remoteManifest' '$targetManifest'
|
||||
chmod +x '$remoteDeploy' '$remoteCheck'
|
||||
sudo -n env GAMECULT_IDUNN_REMOTE_ACTUATOR=1 IDUNN_COMMAND_AUTHORITY=idunn-daemon bash '$remoteDeploy'
|
||||
|
||||
check_ok=0
|
||||
for i in `$(seq 1 30); do
|
||||
if sudo -n bash '$remoteCheck'; then
|
||||
check_ok=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "`$check_ok" -ne 1 ]; then
|
||||
sudo -n bash '$remoteCheck'
|
||||
exit 1
|
||||
fi
|
||||
"@.Replace("`r`n", "`n")
|
||||
[System.IO.File]::WriteAllText($runnerPath, $runnerScript, [System.Text.Encoding]::ASCII)
|
||||
@(
|
||||
"put ""$sourceTarPath"" ""$remoteSourceTarball"""
|
||||
"put ""$cultLibTarPath"" ""$remoteCultLibTarball"""
|
||||
"put ""$DeployScript"" ""$remoteDeploy"""
|
||||
"put ""$CheckScript"" ""$remoteCheck"""
|
||||
"put ""$manifestPath"" ""$remoteManifest"""
|
||||
"put ""$runnerPath"" ""$remoteRunner"""
|
||||
) | Set-Content -Encoding ASCII -LiteralPath $sftpBatchPath
|
||||
|
||||
try {
|
||||
& sftp.exe -b $sftpBatchPath $SshTarget
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "sftp upload failed for yggdrasil-streampixels"
|
||||
}
|
||||
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "chmod +x '$remoteRunner' && bash '$remoteRunner'"
|
||||
$remoteExit = $LASTEXITCODE
|
||||
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $SshTarget "rm -f '$remoteRunner'"
|
||||
if ($remoteExit -ne 0) {
|
||||
throw "remote deploy/check failed for yggdrasil-streampixels"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $runnerPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -LiteralPath $sftpBatchPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "yggdrasil-streampixels deployed with CultLib snapshot."
|
||||
Write-Host " streamPixelsCommit=$streamPixelsCommit"
|
||||
Write-Host " cultLibCommit=$cultLibCommit"
|
||||
Write-Host " artifactSha256=$sourceHash"
|
||||
Write-Host " cultLibSha256=$cultLibHash"
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-mimir-eve-dashboard.cmd archived: Mimir Eve dashboard health is daemon-published over CultNet/RUDP as mimir.cultnet-rudp-provider-health. Do not probe localhost HTTP for daemon health. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-nightwing-eve-browser-reference.cmd archived: Nightwing Eve browser reference health is daemon-published over CultNet/RUDP as nightwing.cultnet-rudp-browser-reference-health. Use health-nightwing-eve-browser-reference.ps1 only as an explicit witness inspection, not Idunn health truth.
|
||||
exit /b 2
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
param(
|
||||
[string] $SshTarget = "nightwing",
|
||||
[string] $ExpectedDocumentRoot = "/opt/gamecult/eve-browser-reference",
|
||||
[string] $RemoteCultCacheWitness = "/var/lib/gamecult/eve-browser-reference/cultcache/eve-browser-reference.service.cc",
|
||||
[int] $MaxWitnessAgeSeconds = 120
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Fail-IdunnHealth {
|
||||
param(
|
||||
[string] $State,
|
||||
[string] $Message
|
||||
)
|
||||
|
||||
Write-Output "idunn.health.state=$State"
|
||||
throw $Message
|
||||
}
|
||||
|
||||
function Invoke-SshText {
|
||||
param(
|
||||
[string] $Command,
|
||||
[string] $FailureMessage,
|
||||
[string] $FailureState = "failed"
|
||||
)
|
||||
|
||||
$output = ssh.exe -o BatchMode=yes -o ConnectTimeout=5 $SshTarget $Command
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($output)) {
|
||||
Fail-IdunnHealth -State $FailureState -Message $FailureMessage
|
||||
}
|
||||
|
||||
return $output
|
||||
}
|
||||
|
||||
function Get-CultCacheInspection {
|
||||
param(
|
||||
[string] $WitnessCopyPath,
|
||||
[string] $InspectorPackageJson
|
||||
)
|
||||
|
||||
$nodeScript = @'
|
||||
const fs = require("fs");
|
||||
const { createRequire } = require("module");
|
||||
|
||||
const witnessCopyPath = process.argv[2];
|
||||
const inspectorPackageJson = process.argv[3];
|
||||
const requireCultCacheInspection = createRequire(inspectorPackageJson);
|
||||
const { inspectCultCacheBytes } = requireCultCacheInspection("cultcache-ts");
|
||||
const inspection = inspectCultCacheBytes(witnessCopyPath, fs.readFileSync(witnessCopyPath));
|
||||
|
||||
function firstRecord(schemaName) {
|
||||
return inspection.records.find((record) => record.schemaName === schemaName) ?? null;
|
||||
}
|
||||
|
||||
function previewArray(record) {
|
||||
return Array.isArray(record?.payloadPreview) ? record.payloadPreview : [];
|
||||
}
|
||||
|
||||
function normalizeRecord(schemaName) {
|
||||
const record = firstRecord(schemaName);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const preview = previewArray(record);
|
||||
switch (schemaName) {
|
||||
case "mimir.eve_browser_reference_manifest":
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
daemonId: preview[0] ?? "",
|
||||
siteTitle: preview[1] ?? "",
|
||||
documentRoot: preview[2] ?? "",
|
||||
entryPoint: preview[3] ?? "",
|
||||
port: preview[4] ?? 0,
|
||||
assetCount: Array.isArray(preview[5]) ? preview[5].length : 0,
|
||||
surfaceTransport: preview[6] ?? "",
|
||||
};
|
||||
case "mimir.eve_browser_reference_static_surface":
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
daemonId: preview[0] ?? "",
|
||||
updatedAtUtc: preview[1] ?? "",
|
||||
documentRoot: preview[2] ?? "",
|
||||
entryPoint: preview[3] ?? "",
|
||||
assetCount: Array.isArray(preview[4]) ? preview[4].length : 0,
|
||||
fixtureCount: Array.isArray(preview[5]) ? preview[5].length : 0,
|
||||
currentCutLine: preview[6] ?? "",
|
||||
};
|
||||
case "mimir.eve_browser_reference_command_boundary":
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
daemonId: preview[0] ?? "",
|
||||
updatedAtUtc: preview[1] ?? "",
|
||||
mode: preview[2] ?? "",
|
||||
writesAccepted: preview[3] === true,
|
||||
operatorInputAuthority: preview[4] ?? "",
|
||||
lifecycleAuthority: preview[5] ?? "",
|
||||
acceptedCommands: Array.isArray(preview[6]) ? preview[6] : [],
|
||||
rejectedCommands: Array.isArray(preview[7]) ? preview[7] : [],
|
||||
};
|
||||
case "mimir.eve_browser_reference_transport_profile":
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
daemonId: preview[0] ?? "",
|
||||
updatedAtUtc: preview[1] ?? "",
|
||||
currentState: preview[2] ?? "",
|
||||
inputTransport: preview[3] ?? "",
|
||||
outputTransport: preview[4] ?? "",
|
||||
healthContract: preview[5] ?? "",
|
||||
idunnRudpHealth: preview[6] ?? "",
|
||||
witnessPath: preview[7] ?? "",
|
||||
currentCutLine: preview[8] ?? "",
|
||||
};
|
||||
case "idunn.daemon_health":
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
daemonId: preview[0] ?? "",
|
||||
state: preview[1] ?? "",
|
||||
detail: preview[2] ?? "",
|
||||
observedAt: preview[3] ?? "",
|
||||
healthContract: preview[4] ?? "",
|
||||
publicationSource: preview[5] ?? "",
|
||||
transport: preview[6] ?? "",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
schemaName: record.schemaName,
|
||||
storedAt: record.storedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({
|
||||
format: inspection.format,
|
||||
catalogSchemaVersions: inspection.catalog.map((entry) => entry.schemaVersion),
|
||||
manifest: normalizeRecord("mimir.eve_browser_reference_manifest"),
|
||||
staticSurface: normalizeRecord("mimir.eve_browser_reference_static_surface"),
|
||||
commandBoundary: normalizeRecord("mimir.eve_browser_reference_command_boundary"),
|
||||
transportProfile: normalizeRecord("mimir.eve_browser_reference_transport_profile"),
|
||||
daemonHealth: normalizeRecord("idunn.daemon_health"),
|
||||
}));
|
||||
'@
|
||||
|
||||
$inspectionJson = $nodeScript | node - $WitnessCopyPath $InspectorPackageJson
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($inspectionJson)) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness inspection failed."
|
||||
}
|
||||
|
||||
try {
|
||||
return $inspectionJson | ConvertFrom-Json
|
||||
} catch {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache inspection returned malformed JSON."
|
||||
}
|
||||
}
|
||||
|
||||
ssh.exe -o BatchMode=yes -o ConnectTimeout=5 $SshTarget "systemctl is-active --quiet nightwing-eve-browser-reference.service"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Fail-IdunnHealth -State "failed" -Message "nightwing-eve-browser-reference.service is not active on Nightwing."
|
||||
}
|
||||
|
||||
$cultCacheStat = Invoke-SshText `
|
||||
-Command "stat -c '%s %Y %U %G' '$RemoteCultCacheWitness' 2>/dev/null" `
|
||||
-FailureMessage "Nightwing Eve browser reference CultCache witness is missing at $RemoteCultCacheWitness."
|
||||
|
||||
$cultCacheParts = $cultCacheStat.Trim() -split '\s+'
|
||||
if ($cultCacheParts.Length -lt 4) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness stat output was malformed: $cultCacheStat"
|
||||
}
|
||||
|
||||
$cultCacheSize = [int64]$cultCacheParts[0]
|
||||
$cultCacheModifiedUtc = [DateTimeOffset]::FromUnixTimeSeconds([int64]$cultCacheParts[1])
|
||||
$cultCacheOwner = $cultCacheParts[2]
|
||||
$cultCacheGroup = $cultCacheParts[3]
|
||||
$cultCacheAgeSeconds = ([DateTimeOffset]::UtcNow - $cultCacheModifiedUtc).TotalSeconds
|
||||
if ($cultCacheSize -le 0) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness at $RemoteCultCacheWitness is empty."
|
||||
}
|
||||
|
||||
if ($cultCacheAgeSeconds -gt $MaxWitnessAgeSeconds) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is stale ($([math]::Round($cultCacheAgeSeconds))s old)."
|
||||
}
|
||||
|
||||
if ($cultCacheOwner -ne "metacrat" -or $cultCacheGroup -ne "metacrat") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference CultCache witness owner is ${cultCacheOwner}:$cultCacheGroup, expected metacrat:metacrat."
|
||||
}
|
||||
|
||||
try {
|
||||
$cultCacheInspectorPackageJson = (Resolve-Path (Join-Path $PSScriptRoot "..\..\CultLib\packages\cultcache-ts\package.json")).Path
|
||||
} catch {
|
||||
Fail-IdunnHealth -State "failed" -Message "CultCache inspector package is missing from the local Odin body."
|
||||
}
|
||||
|
||||
$cultCacheWitnessCopy = Join-Path ([System.IO.Path]::GetTempPath()) ("nightwing-eve-browser-reference-" + [Guid]::NewGuid().ToString("N") + ".cc")
|
||||
try {
|
||||
$remoteCultCacheBase64 = Invoke-SshText `
|
||||
-Command "base64 -w0 '$RemoteCultCacheWitness' 2>/dev/null" `
|
||||
-FailureMessage "Nightwing Eve browser reference CultCache witness could not be copied from Nightwing."
|
||||
[System.IO.File]::WriteAllBytes($cultCacheWitnessCopy, [Convert]::FromBase64String($remoteCultCacheBase64.Trim()))
|
||||
$inspection = Get-CultCacheInspection -WitnessCopyPath $cultCacheWitnessCopy -InspectorPackageJson $cultCacheInspectorPackageJson
|
||||
} catch [FormatException] {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness copy was not valid base64."
|
||||
} finally {
|
||||
if (Test-Path $cultCacheWitnessCopy) {
|
||||
Remove-Item $cultCacheWitnessCopy -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
if ($inspection.format -ne "cultcache.store.v1") {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness has unexpected format '$($inspection.format)'."
|
||||
}
|
||||
|
||||
$requiredCultCacheSchemas = @(
|
||||
"mimir.eve_browser_reference_manifest.v1",
|
||||
"mimir.eve_browser_reference_static_surface.v1",
|
||||
"mimir.eve_browser_reference_command_boundary.v1",
|
||||
"mimir.eve_browser_reference_transport_profile.v1",
|
||||
"idunn.daemon_health.v1"
|
||||
)
|
||||
|
||||
$publishedCultCacheSchemas = @($inspection.catalogSchemaVersions)
|
||||
foreach ($schema in $requiredCultCacheSchemas) {
|
||||
if ($publishedCultCacheSchemas -notcontains $schema) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference CultCache witness is missing schema '$schema'."
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $inspection.manifest) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is missing its manifest record."
|
||||
}
|
||||
|
||||
if ($inspection.manifest.documentRoot -ne $ExpectedDocumentRoot) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference manifest document root is '$($inspection.manifest.documentRoot)', expected '$ExpectedDocumentRoot'."
|
||||
}
|
||||
|
||||
if ($inspection.manifest.entryPoint -ne "index.html") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference manifest entry point is '$($inspection.manifest.entryPoint)', expected 'index.html'."
|
||||
}
|
||||
|
||||
if ($inspection.manifest.surfaceTransport -ne "static-http-lowering") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference manifest transport is '$($inspection.manifest.surfaceTransport)', expected 'static-http-lowering'."
|
||||
}
|
||||
|
||||
if ($null -eq $inspection.staticSurface) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is missing its static-surface record."
|
||||
}
|
||||
|
||||
if ($inspection.staticSurface.documentRoot -ne $ExpectedDocumentRoot) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference static-surface document root is '$($inspection.staticSurface.documentRoot)', expected '$ExpectedDocumentRoot'."
|
||||
}
|
||||
|
||||
if ($null -eq $inspection.commandBoundary) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is missing its command boundary record."
|
||||
}
|
||||
|
||||
if ($inspection.commandBoundary.mode -ne "static-read-only-lowering") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference command boundary mode is '$($inspection.commandBoundary.mode)', expected 'static-read-only-lowering'."
|
||||
}
|
||||
|
||||
if ($inspection.commandBoundary.writesAccepted) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference command boundary unexpectedly accepts writes."
|
||||
}
|
||||
|
||||
if ($inspection.commandBoundary.lifecycleAuthority -ne "idunn-supervisor-command.restart") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference lifecycle authority is '$($inspection.commandBoundary.lifecycleAuthority)', expected 'idunn-supervisor-command.restart'."
|
||||
}
|
||||
|
||||
if ($null -eq $inspection.transportProfile) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is missing its transport profile record."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.currentState -ne "partial-rudp-health-and-provider-store-live") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference transport profile state is '$($inspection.transportProfile.currentState)', expected 'partial-rudp-health-and-provider-store-live'."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.inputTransport -ne "http-static-lowering") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference input transport is '$($inspection.transportProfile.inputTransport)', expected 'http-static-lowering'."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.outputTransport -ne "daemon-owned-cultcache-boundary-store + daemon-published-rudp-health") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference output transport is '$($inspection.transportProfile.outputTransport)', expected 'daemon-owned-cultcache-boundary-store + daemon-published-rudp-health'."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.healthContract -ne "nightwing.cultnet-rudp-browser-reference-health") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference health contract is '$($inspection.transportProfile.healthContract)', expected 'nightwing.cultnet-rudp-browser-reference-health'."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.idunnRudpHealth -ne "idunn.health-published-separately") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference transport profile Idunn health publication is '$($inspection.transportProfile.idunnRudpHealth)', expected 'idunn.health-published-separately'."
|
||||
}
|
||||
|
||||
if ($inspection.transportProfile.witnessPath -ne $RemoteCultCacheWitness) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference transport profile witness path is '$($inspection.transportProfile.witnessPath)', expected '$RemoteCultCacheWitness'."
|
||||
}
|
||||
|
||||
if ($null -eq $inspection.daemonHealth) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference CultCache witness is missing its daemon-health record."
|
||||
}
|
||||
|
||||
if ($inspection.daemonHealth.daemonId -ne "nightwing-eve-browser-reference") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference daemon-health record advertises daemon '$($inspection.daemonHealth.daemonId)', expected 'nightwing-eve-browser-reference'."
|
||||
}
|
||||
|
||||
if ($inspection.daemonHealth.state -ne "healthy") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference daemon-health state is '$($inspection.daemonHealth.state)', expected 'healthy'."
|
||||
}
|
||||
|
||||
if ($inspection.daemonHealth.healthContract -ne "nightwing.cultnet-rudp-browser-reference-health") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference daemon-health contract is '$($inspection.daemonHealth.healthContract)', expected 'nightwing.cultnet-rudp-browser-reference-health'."
|
||||
}
|
||||
|
||||
if ($inspection.daemonHealth.publicationSource -ne "daemon-published") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference daemon-health publication source is '$($inspection.daemonHealth.publicationSource)', expected 'daemon-published'."
|
||||
}
|
||||
|
||||
if ($inspection.daemonHealth.transport -ne "cultnet.transport.rudp.v0") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "Nightwing Eve browser reference daemon-health transport is '$($inspection.daemonHealth.transport)', expected 'cultnet.transport.rudp.v0'."
|
||||
}
|
||||
|
||||
try {
|
||||
$daemonHealthObservedAtUtc = [DateTimeOffset]::Parse($inspection.daemonHealth.observedAt)
|
||||
} catch {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference daemon-health observedAt is malformed: '$($inspection.daemonHealth.observedAt)'."
|
||||
}
|
||||
|
||||
$daemonHealthAgeSeconds = ([DateTimeOffset]::UtcNow - $daemonHealthObservedAtUtc.ToUniversalTime()).TotalSeconds
|
||||
if ($daemonHealthAgeSeconds -gt $MaxWitnessAgeSeconds) {
|
||||
Fail-IdunnHealth -State "failed" -Message "Nightwing Eve browser reference daemon-health record is stale ($([math]::Round($daemonHealthAgeSeconds))s old)."
|
||||
}
|
||||
|
||||
Write-Host "Nightwing Eve browser reference is active, and its daemon-owned CultCache witness now certifies manifest, boundary, and daemon-health truth directly."
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-nightwing-eve-dashboard.cmd archived: Nightwing Eve dashboard broker health is not daemon truth. Rebuild typed CultMesh/Odin dashboard state before restoring a health check. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1 +0,0 @@
|
|||
throw "health-nightwing-eve-dashboard.ps1 archived: Nightwing Eve dashboard broker health is not daemon truth. Inspect typed CultMesh/Odin dashboard state after the publisher is rebuilt."
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-odin.cmd archived: Odin health is daemon-published over CultNet/RUDP as odin.cultnet-rudp-provider-health. Use health-odin.ps1 only as an explicit daemon witness inspection, not Idunn health truth.
|
||||
exit /b 2
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
param(
|
||||
[string] $StateDir = (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\odin"),
|
||||
[int] $MaxSilenceSeconds = 120,
|
||||
[string] $CultNetRudpBind = "0.0.0.0:17871"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$pidPath = Join-Path $StateDir "odin.pid"
|
||||
$outLog = Join-Path $StateDir "odin.out.log"
|
||||
$errLog = Join-Path $StateDir "odin.err.log"
|
||||
$cachePath = Join-Path $StateDir "odin.ccmp"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $pidPath)) {
|
||||
throw "Odin PID file is missing at $pidPath"
|
||||
}
|
||||
|
||||
$pidText = (Get-Content -LiteralPath $pidPath -Raw).Trim()
|
||||
if ($pidText -notmatch '^\d+$') {
|
||||
throw "Odin PID file is invalid at ${pidPath}: '$pidText'"
|
||||
}
|
||||
|
||||
$process = Get-CimInstance Win32_Process -Filter "ProcessId = $pidText" -ErrorAction SilentlyContinue
|
||||
if ($null -eq $process) {
|
||||
throw "Odin process $pidText is not running"
|
||||
}
|
||||
|
||||
foreach ($pattern in @(
|
||||
'node.exe',
|
||||
'src\odin-coordinator.cjs',
|
||||
"--stateDir $StateDir",
|
||||
"--cultnet-rudp-bind $CultNetRudpBind",
|
||||
'--idunn-daemon odin',
|
||||
'--idunn-health-contract odin.cultnet-rudp-provider-health'
|
||||
)) {
|
||||
if ($process.CommandLine -notlike "*$pattern*") {
|
||||
throw "Odin process command line is missing expected segment ${pattern}: $($process.CommandLine)"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($logPath in @($outLog, $errLog)) {
|
||||
if (-not (Test-Path -LiteralPath $logPath)) {
|
||||
throw "Odin log is missing at $logPath"
|
||||
}
|
||||
}
|
||||
|
||||
$newestLogWriteUtc = @($outLog, $errLog) |
|
||||
ForEach-Object { (Get-Item -LiteralPath $_).LastWriteTimeUtc } |
|
||||
Sort-Object -Descending |
|
||||
Select-Object -First 1
|
||||
$logAgeSeconds = ([DateTime]::UtcNow - $newestLogWriteUtc).TotalSeconds
|
||||
if ($logAgeSeconds -gt $MaxSilenceSeconds) {
|
||||
throw "Odin logs have been silent for $([math]::Round($logAgeSeconds))s, exceeding $MaxSilenceSeconds seconds"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $cachePath)) {
|
||||
throw "Odin CultMesh store is missing at $cachePath"
|
||||
}
|
||||
|
||||
$rudpPort = [int](($CultNetRudpBind -split ':')[-1])
|
||||
$udpListener = Get-NetUDPEndpoint -LocalPort $rudpPort -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.OwningProcess -eq [int]$pidText } |
|
||||
Select-Object -First 1
|
||||
if ($null -eq $udpListener) {
|
||||
throw "Odin is not listening on UDP port $rudpPort as process $pidText"
|
||||
}
|
||||
|
||||
Write-Host "Odin healthy: pid=$pidText cultmesh_rudp_udp=$rudpPort store=$cachePath log_age=$([math]::Round($logAgeSeconds))s"
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-raven-capture-mux.cmd archived: Raven capture mux no longer owns Muninn daemon health; Muninn publishes muninn.cultnet-rudp-remote-telemetry-health over CultNet/RUDP.
|
||||
exit /b 2
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
Write-Error "health-raven-capture-mux.ps1 archived: Raven capture mux no longer owns Muninn daemon health; Muninn publishes muninn.cultnet-rudp-remote-telemetry-health over CultNet/RUDP."
|
||||
exit 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-stonks.cmd archived: Stonks health is daemon-published over CultNet/RUDP as stonks.cultnet-rudp-market-health. Do not probe localhost HTTP for daemon health. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-vili.cmd archived: Vili health is daemon-published over CultNet/RUDP as vili.cultnet-rudp-animation-health. Do not probe HTTP for daemon health. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-voidbot.cmd archived: VoidBot health is daemon-published over CultNet/RUDP as voidbot.cultnet-rudp-stack-health. Use health-voidbot.ps1 only as an explicit witness inspection, not Idunn health truth. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
param(
|
||||
[string] $VoidBotRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "VoidBot"),
|
||||
[int] $MaxOrchestratorAgeSeconds = 180,
|
||||
[int] $MaxCultMeshStoreAgeSeconds = 180
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Fail-IdunnHealth {
|
||||
param(
|
||||
[string] $State,
|
||||
[string] $Message
|
||||
)
|
||||
|
||||
Write-Output "idunn.health.state=$State"
|
||||
throw $Message
|
||||
}
|
||||
|
||||
function Read-JsonFile {
|
||||
param([Parameter(Mandatory = $true)][string] $Path)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Get-JsonPropertyValue {
|
||||
param(
|
||||
[Parameter(Mandatory = $false)] $Object,
|
||||
[Parameter(Mandatory = $true)][string] $Name,
|
||||
[Parameter(Mandatory = $false)] $Default = $null
|
||||
)
|
||||
|
||||
if ($null -eq $Object) {
|
||||
return $Default
|
||||
}
|
||||
|
||||
$property = $Object.PSObject.Properties[$Name]
|
||||
if ($null -eq $property) {
|
||||
return $Default
|
||||
}
|
||||
|
||||
return $property.Value
|
||||
}
|
||||
|
||||
function Get-ProcessOrFail {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $Role,
|
||||
[Parameter(Mandatory = $true)][int] $ProcessId
|
||||
)
|
||||
|
||||
$process = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
||||
if ($null -eq $process) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot $Role process PID $ProcessId is not running."
|
||||
}
|
||||
|
||||
return $process
|
||||
}
|
||||
|
||||
function Assert-StoreContains {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $StorePath,
|
||||
[Parameter(Mandatory = $true)][string[]] $Tokens
|
||||
)
|
||||
|
||||
$rg = (Get-Command rg.exe -ErrorAction SilentlyContinue).Source
|
||||
if ([string]::IsNullOrWhiteSpace($rg)) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "ripgrep is required to inspect the VoidBot CultMesh witness store."
|
||||
}
|
||||
|
||||
foreach ($token in $Tokens) {
|
||||
& $rg -a -q --fixed-strings -- $token $StorePath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Fail-IdunnHealth -State "degraded" -Message "VoidBot CultMesh witness store is missing '$token'."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$runtimePath = Join-Path $VoidBotRoot ".voidbot\status\runtime-stack.json"
|
||||
$orchestratorPath = Join-Path $VoidBotRoot ".voidbot\status\gamecult-orchestrator.json"
|
||||
$cultMeshStorePath = Join-Path $VoidBotRoot ".voidbot\status\cultmesh\voidbot-swarm-state.cc"
|
||||
|
||||
$runtime = Read-JsonFile -Path $runtimePath
|
||||
if ($null -eq $runtime) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot runtime state is missing at $runtimePath."
|
||||
}
|
||||
|
||||
if (-not [bool](Get-JsonPropertyValue -Object $runtime -Name "ready" -Default $false)) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot runtime stack reports ready=false."
|
||||
}
|
||||
|
||||
$runtimeStage = [string](Get-JsonPropertyValue -Object $runtime -Name "stage" -Default "unknown")
|
||||
if ($runtimeStage -ne "ready") {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot runtime stack stage is '$runtimeStage', expected 'ready'."
|
||||
}
|
||||
|
||||
$bot = Get-JsonPropertyValue -Object $runtime -Name "bot"
|
||||
$worker = Get-JsonPropertyValue -Object $runtime -Name "worker"
|
||||
$botPid = if ($null -ne $bot) { [int](Get-JsonPropertyValue -Object $bot -Name "pid" -Default 0) } else { 0 }
|
||||
$workerPid = if ($null -ne $worker) { [int](Get-JsonPropertyValue -Object $worker -Name "pid" -Default 0) } else { 0 }
|
||||
if ($botPid -le 0) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot runtime state does not contain a live bot PID."
|
||||
}
|
||||
if ($workerPid -le 0) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot runtime state does not contain a live worker PID."
|
||||
}
|
||||
|
||||
$botProcess = Get-ProcessOrFail -Role "bot" -ProcessId $botPid
|
||||
$workerProcess = Get-ProcessOrFail -Role "worker" -ProcessId $workerPid
|
||||
|
||||
$orchestrator = Read-JsonFile -Path $orchestratorPath
|
||||
if ($null -eq $orchestrator -or $null -eq $orchestrator.organs) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot orchestrator state is missing at $orchestratorPath."
|
||||
}
|
||||
|
||||
$surfaceState = $orchestrator.organs.PSObject.Properties["voidbot-swarm-surface"]
|
||||
if ($null -eq $surfaceState) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot orchestrator does not report the voidbot-swarm-surface organ."
|
||||
}
|
||||
$surfaceState = $surfaceState.Value
|
||||
|
||||
if ($surfaceState.lastStatus -ne "ok") {
|
||||
Fail-IdunnHealth -State "degraded" -Message "VoidBot swarm surface organ lastStatus is '$($surfaceState.lastStatus)', expected 'ok'."
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($surfaceState.lastFinishedAt)) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot swarm surface organ has no lastFinishedAt timestamp."
|
||||
}
|
||||
|
||||
$surfaceFinishedAt = [DateTimeOffset]::Parse($surfaceState.lastFinishedAt).ToUniversalTime()
|
||||
$surfaceAgeSeconds = ([DateTimeOffset]::UtcNow - $surfaceFinishedAt).TotalSeconds
|
||||
if ($surfaceAgeSeconds -gt $MaxOrchestratorAgeSeconds) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot swarm surface organ is stale ($([math]::Round($surfaceAgeSeconds))s old)."
|
||||
}
|
||||
|
||||
$orchestratorFile = Get-Item -LiteralPath $orchestratorPath -ErrorAction SilentlyContinue
|
||||
if ($null -eq $orchestratorFile) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot orchestrator state file is missing at $orchestratorPath."
|
||||
}
|
||||
$orchestratorAgeSeconds = ([DateTimeOffset]::UtcNow - [DateTimeOffset]$orchestratorFile.LastWriteTimeUtc).TotalSeconds
|
||||
if ($orchestratorAgeSeconds -gt $MaxOrchestratorAgeSeconds) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot orchestrator state file is stale ($([math]::Round($orchestratorAgeSeconds))s old)."
|
||||
}
|
||||
|
||||
$cultMeshStore = Get-Item -LiteralPath $cultMeshStorePath -ErrorAction SilentlyContinue
|
||||
if ($null -eq $cultMeshStore) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot CultMesh witness store is missing at $cultMeshStorePath."
|
||||
}
|
||||
if ($cultMeshStore.Length -le 0) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot CultMesh witness store at $cultMeshStorePath is empty."
|
||||
}
|
||||
|
||||
$cultMeshStoreAgeSeconds = ([DateTimeOffset]::UtcNow - [DateTimeOffset]$cultMeshStore.LastWriteTimeUtc).TotalSeconds
|
||||
if ($cultMeshStoreAgeSeconds -gt $MaxCultMeshStoreAgeSeconds) {
|
||||
Fail-IdunnHealth -State "failed" -Message "VoidBot CultMesh witness store is stale ($([math]::Round($cultMeshStoreAgeSeconds))s old)."
|
||||
}
|
||||
|
||||
Assert-StoreContains -StorePath $cultMeshStorePath -Tokens @(
|
||||
"voidbot.provider_advertisement_catalog.v0",
|
||||
"idunn.command_boundary.v1",
|
||||
"idunn.daemon_transport_profile.v1",
|
||||
"voidbot.providers",
|
||||
"voidbot.discord",
|
||||
"voidbot.reddit",
|
||||
"voidbot.archive",
|
||||
"voidbot.source",
|
||||
"voidbot.repo_face",
|
||||
"voidbot.swarm",
|
||||
"command-boundary:voidbot",
|
||||
"transport:voidbot"
|
||||
)
|
||||
|
||||
Write-Host "VoidBot runtime is ready, bot PID $($botProcess.Id) and worker PID $($workerProcess.Id) are live, and the daemon-owned CultMesh witness store is fresh."
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-weksa.cmd archived: Weksa health is daemon-published over CultNet/RUDP as weksa.cultnet-rudp-provider-health. Do not run local command health for daemon truth. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-yggdrasil-heimdall.cmd archived: Heimdall health is daemon-published over CultNet/RUDP as heimdall.cultnet-rudp-provider-health. Use health-yggdrasil-source-app.ps1 only as an explicit deployment witness, not Idunn health truth.
|
||||
exit /b 2
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-yggdrasil-repixelizer.cmd archived: Repixelizer health is daemon-published over CultNet/RUDP as repixelizer.cultnet-rudp-service-health. Use health-yggdrasil-source-app.ps1 only as an explicit deployment witness, not Idunn health truth.
|
||||
exit /b 2
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)] [string] $AppId,
|
||||
[Parameter(Mandatory = $true)] [string] $RepoRoot,
|
||||
[Parameter(Mandatory = $true)] [string] $RemoteAppHome,
|
||||
[Parameter(Mandatory = $true)] [string] $CheckScript,
|
||||
[string] $SshTarget = "yggdrasil"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$localCommit = (git -C $RepoRoot rev-parse HEAD).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($localCommit)) {
|
||||
throw "Could not determine git revision for $RepoRoot"
|
||||
}
|
||||
|
||||
$remoteCheck = "/home/gamecultadmin/$([IO.Path]::GetFileName($CheckScript))"
|
||||
$scpArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
|
||||
$sshArgs = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10", $SshTarget)
|
||||
scp.exe @scpArgs $CheckScript "${SshTarget}:$remoteCheck" | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "scp check script failed for $AppId" }
|
||||
|
||||
$manifest = ssh.exe @sshArgs "cat '$RemoteAppHome/deployment-manifest.txt' 2>/dev/null"
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($manifest)) {
|
||||
throw "$AppId deploy manifest is missing on Yggdrasil."
|
||||
}
|
||||
|
||||
$remoteCommit = ""
|
||||
foreach ($line in $manifest) {
|
||||
if ($line -like "gitCommit=*") {
|
||||
$remoteCommit = $line.Substring("gitCommit=".Length).Trim()
|
||||
}
|
||||
}
|
||||
|
||||
if ($remoteCommit -ne $localCommit) {
|
||||
throw "$AppId deploy is stale. remote=$remoteCommit local=$localCommit"
|
||||
}
|
||||
|
||||
ssh.exe @sshArgs "chmod +x '$remoteCheck' && sudo -n bash '$remoteCheck'"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "$AppId remote health check failed."
|
||||
}
|
||||
|
||||
Write-Host "$AppId is active and deployed at $localCommit."
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo health-yggdrasil-streampixels.cmd archived: StreamPixels health is daemon-published over CultNet/RUDP as streampixels.cultnet-rudp-service-health. Use health-yggdrasil-source-app.ps1 only as an explicit deployment witness, not Idunn health truth.
|
||||
exit /b 2
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "deploy-yggdrasil-heimdall must run as root through Idunn" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source_tar=/srv/heimdall/heimdall-source.tar
|
||||
cultlib_tar=/srv/heimdall/heimdall-cultlib.tar
|
||||
releases=/srv/heimdall/app/releases
|
||||
current=/srv/heimdall/app/current
|
||||
shared_cultlib="$releases/CultLib"
|
||||
compose=/srv/compose/yggdrasil-apps.yaml
|
||||
|
||||
for required in "$source_tar" "$cultlib_tar" "$compose"; do
|
||||
if [ ! -f "$required" ]; then
|
||||
echo "missing audited Heimdall deployment input: $required" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
artifact_sha=$(sha256sum "$source_tar" | awk '{print $1}')
|
||||
release="$releases/sha-${artifact_sha}"
|
||||
previous=$(readlink -f "$current" 2>/dev/null || true)
|
||||
|
||||
if [ ! -f "$release/.idunn-build-complete" ]; then
|
||||
staging="$release.staging.$$"
|
||||
rm -rf "$staging"
|
||||
install -d -o heimdall -g heimdall "$staging" "$shared_cultlib"
|
||||
tar -xf "$source_tar" -C "$staging"
|
||||
rm -rf "$shared_cultlib/packages/cultcache-ts" "$shared_cultlib/packages/cultnet-ts"
|
||||
tar -xf "$cultlib_tar" -C "$shared_cultlib"
|
||||
|
||||
docker run --rm \
|
||||
-v /srv/heimdall:/srv/heimdall \
|
||||
-w "$staging" \
|
||||
node:24.14.1-bookworm-slim@sha256:b506e7321f176aae77317f99d67a24b272c1f09f1d10f1761f2773447d8da26c \
|
||||
sh -c 'corepack enable && pnpm install --frozen-lockfile && pnpm typecheck && pnpm test && pnpm build && pnpm prune --prod'
|
||||
|
||||
printf '%s\n' "$artifact_sha" > "$staging/.idunn-build-complete"
|
||||
chown -R heimdall:heimdall "$staging" "$shared_cultlib"
|
||||
mv "$staging" "$release"
|
||||
fi
|
||||
|
||||
ln -sfn "$release" "$current"
|
||||
chown -h heimdall:heimdall "$current"
|
||||
|
||||
if ! docker compose -f "$compose" up -d --force-recreate heimdall; then
|
||||
if [ -n "$previous" ] && [ -d "$previous" ]; then
|
||||
ln -sfn "$previous" "$current"
|
||||
chown -h heimdall:heimdall "$current"
|
||||
docker compose -f "$compose" up -d --force-recreate heimdall || true
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
healthy=0
|
||||
for _ in $(seq 1 30); do
|
||||
if curl -fsS http://127.0.0.1:4100/healthz >/dev/null; then
|
||||
healthy=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "$healthy" -ne 1 ]; then
|
||||
echo "Heimdall failed health verification after deploying $artifact_sha" >&2
|
||||
if [ -n "$previous" ] && [ -d "$previous" ]; then
|
||||
ln -sfn "$previous" "$current"
|
||||
chown -h heimdall:heimdall "$current"
|
||||
docker compose -f "$compose" up -d --force-recreate heimdall || true
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Heimdall deployed audited source artifact $artifact_sha"
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo restart-nightwing-eve-browser-reference.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo restart-nightwing-eve-browser-reference.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File %~dp0restart-nightwing-eve-browser-reference.ps1
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
param(
|
||||
[string] $SshTarget = "nwroot",
|
||||
[string] $Service = "nightwing-eve-browser-reference.service"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
||||
throw "restart-nightwing-eve-browser-reference.ps1 is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path."
|
||||
}
|
||||
|
||||
ssh.exe -o BatchMode=yes -o ConnectTimeout=5 $SshTarget "systemctl restart $Service && systemctl is-active --quiet $Service"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Remote restart failed for $Service on $SshTarget with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
Write-Host "Restarted $Service on $SshTarget."
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@echo off
|
||||
echo restart-nightwing-eve-dashboard.cmd archived: Mimir.EveDashboard's HTTP/WebSocket broker is cut. Rebuild a typed CultMesh/Odin dashboard publisher before restoring Idunn lifecycle authority. 1>&2
|
||||
exit /b 2
|
||||
|
|
@ -1 +0,0 @@
|
|||
throw "restart-nightwing-eve-dashboard.ps1 archived: Mimir.EveDashboard's HTTP/WebSocket broker is cut. Rebuild a typed CultMesh/Odin dashboard publisher before restoring Idunn lifecycle authority."
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo restart-odin.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo restart-odin.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File %~dp0restart-odin.ps1
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
param(
|
||||
[string] $StateDir = (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\odin"),
|
||||
[string] $CultNetRudpBind = "0.0.0.0:17871",
|
||||
[string] $IdunnRudpHealth = $(if ($env:ODIN_IDUNN_RUDP_HEALTH) { $env:ODIN_IDUNN_RUDP_HEALTH } else { $env:IDUNN_RUDP_HEALTH }),
|
||||
[string] $IdunnDaemon = "odin",
|
||||
[string] $IdunnHealthContract = "odin.cultnet-rudp-provider-health"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
||||
throw "restart-odin.ps1 is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path."
|
||||
}
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$stopScript = Join-Path $PSScriptRoot "stop-odin.ps1"
|
||||
$startScript = Join-Path $PSScriptRoot "start-odin.ps1"
|
||||
$powerShellExe = Join-Path $env:WINDIR "System32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $powerShellExe)) {
|
||||
throw "PowerShell executable is missing at $powerShellExe"
|
||||
}
|
||||
|
||||
$startArgs = @(
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"-File", $startScript,
|
||||
"-StateDir", $StateDir,
|
||||
"-CultNetRudpBind", $CultNetRudpBind,
|
||||
"-IdunnDaemon", $IdunnDaemon,
|
||||
"-IdunnHealthContract", $IdunnHealthContract
|
||||
)
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($IdunnRudpHealth)) {
|
||||
$startArgs += @("-IdunnRudpHealth", $IdunnRudpHealth)
|
||||
}
|
||||
|
||||
& $powerShellExe -NoProfile -ExecutionPolicy Bypass -File $stopScript -StateDir $StateDir
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "stop-odin.ps1 failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
& $powerShellExe @startArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "start-odin.ps1 failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo restart-stonks.cmd is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo restart-stonks.cmd is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File %~dp0restart-stonks.ps1
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$stonksRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "Stonks")
|
||||
$stateDir = Join-Path $stonksRoot "scratch\stonks"
|
||||
$pidPath = Join-Path $stateDir "stonks.pid"
|
||||
$startScript = Join-Path $stonksRoot "scripts\start-stonks.ps1"
|
||||
|
||||
if (Test-Path -LiteralPath $pidPath) {
|
||||
$pidText = (Get-Content -LiteralPath $pidPath -Raw).Trim()
|
||||
if ($pidText -match "^\d+$") {
|
||||
$process = Get-Process -Id ([int] $pidText) -ErrorAction SilentlyContinue
|
||||
if ($null -ne $process) {
|
||||
Stop-Process -Id $process.Id -Force
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
}
|
||||
Remove-Item -LiteralPath $pidPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $startScript
|
||||
exit $LASTEXITCODE
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo restart-vili.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo restart-vili.cmd is an Idunn actuator wrapper. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "%~dp0restart-vili.ps1" %*
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
param(
|
||||
[string] $RavenHost = "raven",
|
||||
[string] $LocalViliRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "Vili"),
|
||||
[string] $LocalCultLibRoot = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "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
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@echo off
|
||||
if not "%IDUNN_ACTUATOR%"=="1" (
|
||||
echo restart-weksa.cmd is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
if /I not "%IDUNN_COMMAND_AUTHORITY%"=="idunn-daemon" (
|
||||
echo restart-weksa.cmd is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path. 1>&2
|
||||
exit /b 1
|
||||
)
|
||||
call %~dp0..\..\weksa\scripts\restart-weksa-daemon.cmd
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
param(
|
||||
[string] $StateDir = (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\odin"),
|
||||
[string] $CultNetRudpBind = "0.0.0.0:17871",
|
||||
[string] $IdunnRudpHealth = $(if ($env:ODIN_IDUNN_RUDP_HEALTH) { $env:ODIN_IDUNN_RUDP_HEALTH } else { $env:IDUNN_RUDP_HEALTH }),
|
||||
[string] $IdunnDaemon = "odin",
|
||||
[string] $IdunnHealthContract = "odin.cultnet-rudp-provider-health",
|
||||
[switch] $Foreground
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$scriptPath = Join-Path $repoRoot "src\odin-coordinator.cjs"
|
||||
$pidPath = Join-Path $StateDir "odin.pid"
|
||||
$outLog = Join-Path $StateDir "odin.out.log"
|
||||
$errLog = Join-Path $StateDir "odin.err.log"
|
||||
$nodeExe = (Get-Command node.exe -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Source)
|
||||
if (-not $nodeExe) {
|
||||
$nodeExe = "C:\Program Files\nodejs\node.exe"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $nodeExe)) {
|
||||
throw "Node.js executable is missing; checked PATH and $nodeExe"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($IdunnRudpHealth)) {
|
||||
throw "Odin Idunn health publication requires -IdunnRudpHealth, ODIN_IDUNN_RUDP_HEALTH, or IDUNN_RUDP_HEALTH; no localhost default is assumed."
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $StateDir | Out-Null
|
||||
|
||||
if (Test-Path $pidPath) {
|
||||
$oldPid = (Get-Content -Raw $pidPath).Trim()
|
||||
if ($oldPid -match "^\d+$") {
|
||||
$old = Get-Process -Id ([int] $oldPid) -ErrorAction SilentlyContinue
|
||||
if ($old) {
|
||||
throw "Odin already appears to be running as PID $oldPid."
|
||||
}
|
||||
}
|
||||
Remove-Item -LiteralPath $pidPath -Force
|
||||
}
|
||||
|
||||
$env:NODE_PATH = (Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) "CultLib\packages")
|
||||
$args = @(
|
||||
$scriptPath,
|
||||
"--stateDir", $StateDir,
|
||||
"--cultnet-rudp-bind", $CultNetRudpBind,
|
||||
"--idunn-rudp-health", $IdunnRudpHealth,
|
||||
"--idunn-daemon", $IdunnDaemon,
|
||||
"--idunn-health-contract", $IdunnHealthContract
|
||||
)
|
||||
|
||||
if ($Foreground) {
|
||||
& $nodeExe @args
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
$proc = Start-Process -FilePath $nodeExe -ArgumentList $args -WorkingDirectory $repoRoot -WindowStyle Hidden -PassThru -RedirectStandardOutput $outLog -RedirectStandardError $errLog
|
||||
$proc.Id | Set-Content -Encoding ASCII -LiteralPath $pidPath
|
||||
Start-Sleep -Seconds 1
|
||||
if ($proc.HasExited) {
|
||||
$detail = ""
|
||||
if (Test-Path $outLog) { $detail += Get-Content -Raw $outLog }
|
||||
if (Test-Path $errLog) { $detail += Get-Content -Raw $errLog }
|
||||
throw "Odin exited immediately with code $($proc.ExitCode).`n$detail"
|
||||
}
|
||||
|
||||
Write-Host "Odin started as PID $($proc.Id)."
|
||||
Write-Host "CultMesh/RUDP document catalog: $CultNetRudpBind"
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
param(
|
||||
[string] $StateDir = (Join-Path (Split-Path -Parent $PSScriptRoot) "scratch\odin")
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$pidPath = Join-Path $StateDir "odin.pid"
|
||||
if (-not (Test-Path $pidPath)) {
|
||||
Write-Host "No Odin PID file found at $pidPath."
|
||||
exit 0
|
||||
}
|
||||
|
||||
$odinPid = (Get-Content -Raw $pidPath).Trim()
|
||||
if ($odinPid -match "^\d+$") {
|
||||
$process = Get-Process -Id ([int] $odinPid) -ErrorAction SilentlyContinue
|
||||
if ($process) {
|
||||
Stop-Process -Id $process.Id
|
||||
Write-Host "Stopped Odin PID $odinPid."
|
||||
}
|
||||
}
|
||||
|
||||
Remove-Item -LiteralPath $pidPath -Force
|
||||
Loading…
Reference in a new issue