igneum/relay/playbooks/ember-installed-tune-pc1.ps1

94 lines
8.8 KiB
PowerShell

# Ember: the SHIPPED app tunes itself (6 October 2026). Unelevated, no kit, no second engine, no --stop-miners:
# the installed app (0.3.14 or later: Ember Tune, the Power Helper task, /api/tune/goal, the climb) runs one tune
# per card through the registered task (no prompt) while it mines. This script only asks and watches:
# POST api/tune/goal (the climb on, balanced) and POST api/sweep/start per card, then reads api/state and the app's
# own log for the TUNE rows. It never quits, pauses or resumes the installed app (the 5 October rule) and never
# touches the cards itself. At the end the climb switch goes back to what it was.
# Order: the AMD card's ladder first (the ADLX apply-on-ack fix, bd7fcf4), then each NVIDIA card's climb.
$ErrorActionPreference = 'Continue'
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
$u = Join-Path $appDir 'app.url'
if (-not (Test-Path -LiteralPath $u)) { Write-Output 'RESULT TUNE error=no_app_url (installed app not running)'; exit 1 }
$base = (Get-Content -LiteralPath $u -Raw).Trim()
function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } }
function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($base + $path) -Method POST -Body $json -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null; return $true } catch { Say ($path + ': ' + $_.Exception.Message); return $false } }
$s = State
if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 }
$ver = [string]$s.version
# the first line: the version gate (main, 6 October 2026): 0.3.14 or later carries bd7fcf4 (AMD apply on ack) and
# 5165de7 (api/tune/goal with the climb); 0.3.15 the helper fixes A to E (6 October 2026); an older app aborts here
$vok = $false; try { $vok = ([version]($ver -replace '[^0-9.].*$', '')) -ge [version]'0.3.15' } catch { }
Write-Output ('RESULT TUNE version=' + $ver + ' gate=' + $(if ($vok) { 'ok (0.3.15 or later)' } else { 'FAILED (needs 0.3.15: the helper fixes A to E)' }) + ' climb_field=' + $(if ($null -ne $s.settings.tune_climb) { 'present' } else { 'absent' }))
if (-not $vok -or $null -eq $s.settings.tune_climb) { Write-Output 'RESULT TUNE error=version_gate'; exit 1 }
Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal)
$t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' }))
if (-not $t) { Write-Output 'RESULT TUNE error=task_not_registered (the NVIDIA climbs would need a prompt; stopping)'; exit 1 }
if (-not $s.settings.power_control) { Write-Output 'RESULT TUNE error=power_control_off'; exit 1 }
$climbBefore = [bool]$s.settings.tune_climb
$cards = @($s.mining.cards | Where-Object { $_.enabled -and ($_.vendor -eq 'nvidia' -or $_.vendor -eq 'amd') -and $_.name -notmatch 'Radeon\(TM\) Graphics' })
Write-Output ('RESULT TUNE cards ' + (($cards | ForEach-Object { $_.key + ' (' + $_.name + ', ' + $_.vendor + ', sweep_state ' + $_.sweep_state + ', line "' + $_.tune_line + '")' }) -join ' | '))
foreach ($c in $cards) { Write-Output ('RESULT TUNE before key=' + $c.key + ' power_w=' + $c.power_w + ' power_pct=' + $c.power_pct + ' clock_cap=' + $c.clock_cap_mhz + ' mhs=' + $c.mhs + ' line=' + $c.tune_line) }
# the climb on (NVIDIA cards; an AMD card has no clock knob and runs its ladder regardless)
if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Output 'RESULT TUNE error=tune_goal_refused'; exit 1 }
Start-Sleep -Seconds 2
$order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' })
$deadline = (Get-Date).AddMinutes(27)
$logs = Join-Path $env:LOCALAPPDATA 'igneum\logs' # the app's log folder is <data root>\logs (platform::log_dir), not app\logs (6 October 2026: the first window read no log at all)
function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } }
function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) }
$rows = 0
$m0 = LogMark
# run 6's ladder floors (6 October 2026, PC 1), what the climb has to beat
$floor = @{ 'NVIDIA GeForce RTX 5090' = @{ eff = 0.5632; mhs = 127.71; watts = 226.8; point = '1854 MHz at 100%' }; 'NVIDIA GeForce RTX 4070' = @{ eff = 0.3809; mhs = 28.78; watts = 75.6; point = '1863 MHz at 50%' } }
foreach ($c in $order) {
if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break }
$m = LogMark
$body = '{"key":"' + ($c.key -replace '"', '\"') + '"}'
if (-not (Post 'api/sweep/start' $body)) { Write-Output ('RESULT TUNE error=sweep_start_refused key=' + $c.key); continue }
Say ('tune asked for ' + $c.key)
$seen = 0; $done = $false; $started = Get-Date
while (-not $done -and (Get-Date) -lt $deadline) {
Start-Sleep -Seconds 10
$new = @(LogSince $m)
while ($seen -lt $new.Count) { $l = [string]$new[$seen]; $seen++; if ($l -match 'TUNE (card=|chosen|start|aborted|progress)') { if ($l -notmatch 'TUNE progress') { Write-Output ('RESULT ' + ($l -replace '^.*?(TUNE )', 'TUNE ')) }; if ($l -match 'TUNE card=') { $rows++ } } }
$st = State
if ($st) {
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
if ($cc) {
$running = ($cc.sweep_state -eq 'running')
if (-not $running -and ((Get-Date) - $started).TotalSeconds -gt 180) { $done = $true }
if ($running -and $cc.tune_steps -gt 0 -and ($seen % 6 -eq 0)) { Say ($c.key + ': step ' + $cc.tune_step + ' of ' + $cc.tune_steps + ', ' + $cc.sweep_note) }
}
}
}
$st = State
if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } }
}
# the prompt count: the app's own log over the window; a task line says "(no prompt)", a prompted helper says
# "administrator prompt" (the Security log's process audit is off on PC 1, so the app's log is the record)
$win = @(LogSince $m0)
$noPrompt = @($win | Where-Object { $_ -match 'Power Helper task \(no prompt\)' }).Count
$prompted = @($win | Where-Object { $_ -match 'administrator prompt|runas|UAC' }).Count
Write-Output ('RESULT TUNE prompts=' + $prompted + ' task_lines=' + $noPrompt + ' (from the app log over the window)')
# the climb against run 6's ladder floor, per NVIDIA card
$st = State
if ($st) {
foreach ($c in @($cards | Where-Object { $_.vendor -eq 'nvidia' })) {
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
$f = $floor[[string]$c.name]
if ($cc -and $f -and $cc.sweep_eff -gt 0) {
$d = [math]::Round(($cc.sweep_eff - $f.eff) / $f.eff * 100, 1)
Write-Output ('RESULT TUNE versus_run6 key=' + $c.key + ' climb=' + $cc.sweep_eff + ' MH/W (' + $cc.sweep_mhs + ' MH/s at ' + $cc.sweep_watts + ' W) ladder_floor=' + $f.eff + ' (' + $f.point + ') delta=' + $d + '% ' + $(if ($cc.sweep_eff -gt $f.eff) { 'the climb beat the floor' } elseif ($cc.sweep_eff -eq $f.eff) { 'equal' } else { 'the ladder floor stands' }))
} elseif ($cc -and $f) { Write-Output ('RESULT TUNE versus_run6 key=' + $c.key + ' no chosen point this run (' + $cc.sweep_note + ')') }
}
}
# the climb switch back to what it was
Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null
Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows)
$smi = Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'
if (-not (Test-Path $smi)) { $smi = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
if (Test-Path $smi) { Write-Output ('RESULT TUNE nvidia_now ' + ((& $smi --query-gpu=index,name,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
if ($rows -eq 0) { Write-Output 'RESULT TUNE error=no_rows'; exit 1 }
exit 0