Files
awoooi/agent99-bootstrap.ps1

231 lines
8.2 KiB
PowerShell

param(
[string]$AgentRoot = "C:\Wooo\Agent99",
[string]$SourceRevision = "",
[switch]$InstallScheduledTasks
)
$ErrorActionPreference = "Stop"
$SourceRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$BinDir = Join-Path $AgentRoot "bin"
$ConfigDir = Join-Path $AgentRoot "config"
$EvidenceDir = Join-Path $AgentRoot "evidence"
$LogDir = Join-Path $AgentRoot "logs"
$QueueDir = Join-Path $AgentRoot "queue"
$RequestDir = Join-Path $AgentRoot "requests"
$AlertDir = Join-Path $AgentRoot "alerts"
$PlaybookDir = Join-Path $AgentRoot "playbooks"
$DashboardDir = Join-Path $AgentRoot "dashboard"
$StateDir = Join-Path $AgentRoot "state"
foreach ($dir in @($AgentRoot, $BinDir, $ConfigDir, $EvidenceDir, $LogDir, $QueueDir, $RequestDir, $AlertDir, $PlaybookDir, $DashboardDir, $StateDir)) {
New-Item -ItemType Directory -Force -Path $dir | Out-Null
}
function Copy-AgentFile {
param([string]$Name)
$src = Join-Path $SourceRoot $Name
$dst = Join-Path $BinDir $Name
if (Test-Path $src) {
$srcFull = [System.IO.Path]::GetFullPath($src)
$dstFull = [System.IO.Path]::GetFullPath($dst)
if ($srcFull -ne $dstFull) {
if ([System.IO.Path]::GetExtension($src).ToLowerInvariant() -eq ".ps1") {
$content = [System.IO.File]::ReadAllText($src)
$utf8Bom = [System.Text.UTF8Encoding]::new($true)
[System.IO.File]::WriteAllText($dst, $content, $utf8Bom)
} else {
Copy-Item -Force $src $dst
}
}
}
}
$agentFiles = @(
"agent99-control-plane.ps1",
"agent99-register-tasks.ps1",
"agent99-submit-request.ps1",
"agent99-telegram-inbox.ps1",
"agent99-sre-alert-inbox.ps1",
"agent99-sre-alert-relay.ps1",
"host-reboot-scorecard.ps1"
)
foreach ($agentFile in $agentFiles) {
Copy-AgentFile $agentFile
}
$resolvedSourceRevision = $SourceRevision
if (-not $resolvedSourceRevision) {
try {
$gitRevision = & git -C $SourceRoot rev-parse HEAD 2>$null
if ($LASTEXITCODE -eq 0 -and $gitRevision) {
$resolvedSourceRevision = ([string]$gitRevision).Trim()
}
} catch {
$resolvedSourceRevision = ""
}
}
if (-not $resolvedSourceRevision) { $resolvedSourceRevision = "unknown" }
$manifestRows = @()
foreach ($agentFile in $agentFiles) {
$sourcePath = Join-Path $SourceRoot $agentFile
$runtimePath = Join-Path $BinDir $agentFile
$sourceExists = Test-Path $sourcePath
$runtimeExists = Test-Path $runtimePath
$sourceSha256 = if ($sourceExists) { (Get-FileHash -Algorithm SHA256 -LiteralPath $sourcePath).Hash.ToLowerInvariant() } else { $null }
$runtimeSha256 = if ($runtimeExists) { (Get-FileHash -Algorithm SHA256 -LiteralPath $runtimePath).Hash.ToLowerInvariant() } else { $null }
$contentMatched = if ($sourceExists -and $runtimeExists) { [System.IO.File]::ReadAllText($sourcePath) -ceq [System.IO.File]::ReadAllText($runtimePath) } else { $false }
$manifestRows += [pscustomobject]@{
name = $agentFile
sourceExists = $sourceExists
runtimeExists = $runtimeExists
sourceSha256 = $sourceSha256
runtimeSha256 = $runtimeSha256
contentMatched = [bool]$contentMatched
matched = [bool]$contentMatched
}
}
$runtimeMatched = [bool](@($manifestRows).Count -eq @($agentFiles).Count -and @($manifestRows | Where-Object { -not $_.matched }).Count -eq 0)
$runtimeManifest = [pscustomobject]@{
schemaVersion = "agent99_runtime_manifest_v1"
generatedAt = (Get-Date -Format o)
sourceRevision = $resolvedSourceRevision
runtimeMatched = $runtimeMatched
fileCount = @($manifestRows).Count
mismatchCount = @($manifestRows | Where-Object { -not $_.matched }).Count
files = $manifestRows
}
$manifestPath = Join-Path $StateDir "runtime-manifest.json"
$manifestEvidencePath = Join-Path $EvidenceDir ("agent99-RuntimeManifest-" + (Get-Date -Format "yyyyMMdd-HHmmss") + ".json")
$runtimeManifest | ConvertTo-Json -Depth 8 | Set-Content -Encoding UTF8 $manifestPath
$runtimeManifest | ConvertTo-Json -Depth 8 | Set-Content -Encoding UTF8 $manifestEvidencePath
if (-not $runtimeMatched) {
throw "Agent99 runtime manifest mismatch; inspect $manifestEvidencePath"
}
$exampleConfig = Join-Path $SourceRoot "agent99.config.example.json"
$configPath = Join-Path $ConfigDir "agent99.config.json"
if (-not (Test-Path $configPath)) {
if (Test-Path $exampleConfig) {
Copy-Item $exampleConfig $configPath
} else {
@"
{
"agentName": "Agent99",
"agentRoot": "$($AgentRoot.Replace('\', '\\'))",
"sshUser": "wooo",
"sshUsers": {},
"vmrunPath": "",
"hosts": ["192.168.0.110", "192.168.0.112", "192.168.0.120", "192.168.0.121", "192.168.0.188"],
"vms": [],
"publicUrls": ["awoooi.wooo.work", "stock.wooo.work", "2026fifa.wooo.work", "gitea.wooo.work", "harbor.wooo.work"],
"sreAlertRelay": {
"enabled": true,
"prefix": "http://+:8787/agent99/sre-alert/",
"tokenEnv": "AGENT99_SRE_RELAY_TOKEN",
"allowedRemotePrefixes": ["127.", "::1", "192.168.0."]
},
"telegram": {
"enabled": true,
"botTokenEnv": "AGENT99_TELEGRAM_BOT_TOKEN",
"chatIdEnv": "AGENT99_TELEGRAM_CHAT_ID",
"botTokenEnvAliases": ["AWOOOI_TG_BOT_TOKEN", "OPENCLAW_TG_BOT_TOKEN", "TELEGRAM_BOT_TOKEN", "TELEGRAM_TOKEN", "TG_BOT_TOKEN", "AIDER_WATCH_TELEGRAM_TOKEN"],
"chatIdEnvAliases": ["TELEGRAM_CHAT_ID", "TELEGRAM_ALERT_CHAT_ID", "TELEGRAM_P0_CHAT_ID", "TELEGRAM_SECURITY_CHAT_ID", "OPENCLAW_TG_CHAT_ID", "TG_CHAT_ID", "AIDER_WATCH_TELEGRAM_CHAT_ID"],
"relay": { "enabled": true, "host": "192.168.0.110", "container": "stockplatform-v2-api-1" },
"alertAllOperations": false,
"alertInfoEvents": false,
"alertOperatorCommands": true,
"alertSreCommandResults": true,
"dedupeMinutes": 30
},
"selfHealth": {
"scheduledTasks": [
"Wooo-Agent99-Startup-Recovery",
"Wooo-Agent99-Heartbeat",
"Wooo-Agent99-Performance-Guard",
"Wooo-Agent99-Control-Loop",
"Wooo-Agent99-Telegram-Inbox",
"Wooo-Agent99-SRE-Alert-Inbox",
"Wooo-Agent99-SRE-Alert-Relay",
"Wooo-Agent99-Self-Health",
"Wooo-Agent99-Backup-Health"
]
},
"performance": {
"enabled": true,
"intervalMinutes": 1,
"loadPerCoreWarning": 0.9,
"loadPerCoreCritical": 1.5,
"memoryAvailablePercentCritical": 10,
"diskUsedPercentWarning": 90,
"diskUsedPercentCritical": 95,
"captureTopCpuOnWarning": true,
"topCpuTimeoutSeconds": 10,
"loadShedding": { "enabled": true, "actions": [] }
},
"backupHealth": {
"host": "192.168.0.110",
"targets": [],
"fileChecks": [],
"cronPatterns": []
}
}
"@ | Set-Content -Encoding UTF8 $configPath
}
}
$launcher = Join-Path $AgentRoot "agent99-run.ps1"
@"
param(
[ValidateSet("Status", "Recover", "StartVMs", "PublicSmoke", "HarborRepair", "AwoooRepair", "Perf", "LoadShed", "SelfCheck", "BackupCheck", "ProviderFreshness", "SecurityTriage", "ControlTick")]
[string]`$Mode = "Status",
[switch]`$ControlledApply,
[switch]`$SuppressAlerts,
[string]`$SuppressReason = ""
)
& "$BinDir\agent99-control-plane.ps1" -Mode `$Mode -ControlledApply:`$ControlledApply -SuppressAlerts:`$SuppressAlerts -SuppressReason `$SuppressReason -ConfigPath "$configPath" -EvidenceDir "$EvidenceDir"
"@ | Set-Content -Encoding UTF8 $launcher
$submitCmd = Join-Path $AgentRoot "agent99-submit-request.cmd"
@"
@echo off
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$BinDir\agent99-submit-request.ps1" -RunNow
pause
"@ | Set-Content -Encoding ASCII $submitCmd
function Find-Vmrun {
$candidates = @(
"C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe",
"C:\Program Files\VMware\VMware Workstation\vmrun.exe"
)
foreach ($candidate in $candidates) {
if (Test-Path $candidate) { return $candidate }
}
return ""
}
$vmrun = Find-Vmrun
if ($vmrun) {
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$config.vmrunPath = $vmrun
$config | ConvertTo-Json -Depth 8 | Set-Content -Encoding UTF8 $configPath
}
if ($InstallScheduledTasks) {
& (Join-Path $BinDir "agent99-register-tasks.ps1") -AgentRoot $AgentRoot
}
Write-Host "Agent99 bootstrap complete"
Write-Host "AgentRoot: $AgentRoot"
Write-Host "Config: $configPath"
Write-Host "Evidence: $EvidenceDir"
Write-Host "Runtime manifest: $manifestPath"
Write-Host "Source revision: $resolvedSourceRevision"
if ($vmrun) {
Write-Host "vmrun: $vmrun"
} else {
Write-Host "vmrun: not detected; update agent99.config.json"
}