param( [int] $Limit = 25, [int] $TimeoutMs = 2500 ) $ErrorActionPreference = "Stop" $ConfigPath = "C:\MinerOpsAgent\agent-config.json" if (-not (Test-Path $ConfigPath)) { throw "Missing config file: $ConfigPath" } $Config = Get-Content $ConfigPath -Raw | ConvertFrom-Json $HubUrl = [string]$Config.hub_url $HubUrl = $HubUrl.TrimEnd("/") $AgentId = [string]$Config.agent_id $SiteName = [string]$Config.site_name $Secret = [string]$Config.agent_secret $Headers = @{ "X-MinerOps-Agent-Secret" = $Secret } function Invoke-WhatsMinerApi3Read { param( [Parameter(Mandatory=$true)] [string] $Ip, [Parameter(Mandatory=$true)] [hashtable] $Request, [int] $TimeoutMs = 2500 ) $client = $null $stream = $null try { $json = ($Request | ConvertTo-Json -Depth 20 -Compress) $payload = [System.Text.Encoding]::ASCII.GetBytes($json) $length = [BitConverter]::GetBytes([Int32]$payload.Length) $client = New-Object System.Net.Sockets.TcpClient $client.ReceiveTimeout = $TimeoutMs $client.SendTimeout = $TimeoutMs $connect = $client.BeginConnect($Ip, 4433, $null, $null) if (-not $connect.AsyncWaitHandle.WaitOne($TimeoutMs, $false)) { try { $client.Close() } catch {} throw "Timed out connecting to $Ip`:4433" } $client.EndConnect($connect) $stream = $client.GetStream() $stream.ReadTimeout = $TimeoutMs $stream.WriteTimeout = $TimeoutMs $stream.Write($length, 0, 4) $stream.Write($payload, 0, $payload.Length) $stream.Flush() $lenBuf = New-Object byte[] 4 $offset = 0 while ($offset -lt 4) { $read = $stream.Read($lenBuf, $offset, 4 - $offset) if ($read -le 0) { throw "No response length received." } $offset += $read } $responseLength = [BitConverter]::ToInt32($lenBuf, 0) if ($responseLength -le 0 -or $responseLength -gt 1048576) { throw "Invalid response length: $responseLength" } $buffer = New-Object byte[] $responseLength $offset = 0 while ($offset -lt $responseLength) { $read = $stream.Read($buffer, $offset, $responseLength - $offset) if ($read -le 0) { throw "Response ended early at $offset of $responseLength bytes." } $offset += $read } $raw = [System.Text.Encoding]::ASCII.GetString($buffer, 0, $responseLength) $parsed = $raw | ConvertFrom-Json return [pscustomobject]@{ ok = $true parsed = $parsed error = $null } } catch { return [pscustomobject]@{ ok = $false parsed = $null error = $_.Exception.Message } } finally { if ($stream) { try { $stream.Close() } catch {} } if ($client) { try { $client.Close() } catch {} } } } function Invoke-Api3OkCommand { param( [string] $Ip, [hashtable] $Request ) $result = Invoke-WhatsMinerApi3Read -Ip $Ip -Request $Request -TimeoutMs $TimeoutMs if (-not $result.ok) { return $null } if ($result.parsed.code -ne 0) { return $null } return $result.parsed } Write-Host "Fetching known miners from $HubUrl" $known = Invoke-RestMethod ` -Uri "$HubUrl/api/agent/known-miners" ` -Headers $Headers ` -Method Get $miners = @($known.miners) if ($Limit -gt 0) { $miners = @($miners | Select-Object -First $Limit) } Write-Host "API3 enrichment check count: $($miners.Count)" $items = New-Object System.Collections.Generic.List[object] $checked = 0 $api3Ok = 0 $api3Failed = 0 foreach ($miner in $miners) { $checked += 1 $ip = [string]$miner.ip_address $mac = [string]$miner.mac_address if (-not $ip) { continue } Write-Host "[$checked/$($miners.Count)] $ip $mac" $deviceInfoResult = Invoke-WhatsMinerApi3Read ` -Ip $ip ` -Request @{ cmd = "get.device.info" } ` -TimeoutMs $TimeoutMs if (-not $deviceInfoResult.ok -or $deviceInfoResult.parsed.code -ne 0) { $api3Failed += 1 $err = $deviceInfoResult.error if ($deviceInfoResult.ok) { $err = "API3 code $($deviceInfoResult.parsed.code): $($deviceInfoResult.parsed.msg)" } $items.Add([pscustomobject]@{ ip = $ip mac = $mac api3_supported = $false api3_error = $err device_info = $null miner_setting = $null miner_status = $null fan_setting = $null custom_data = $null }) continue } $api3Ok += 1 $minerSetting = Invoke-Api3OkCommand ` -Ip $ip ` -Request @{ cmd = "get.miner.setting" } $minerStatus = Invoke-Api3OkCommand ` -Ip $ip ` -Request @{ cmd = "get.miner.status"; param = "summary+pools+edevs" } $fanSetting = Invoke-Api3OkCommand ` -Ip $ip ` -Request @{ cmd = "get.fan.setting" } $customData = Invoke-Api3OkCommand ` -Ip $ip ` -Request @{ cmd = "get.device.custom_data" } $items.Add([pscustomobject]@{ ip = $ip mac = $mac api3_supported = $true api3_error = $null device_info = $deviceInfoResult.parsed miner_setting = $minerSetting miner_status = $minerStatus fan_setting = $fanSetting custom_data = $customData }) } $payload = @{ agent_id = $AgentId site_name = $SiteName items = @($items.ToArray()) } $json = $payload | ConvertTo-Json -Depth 80 Write-Host "Posting API3 enrichment results to hub..." $result = Invoke-RestMethod ` -Uri "$HubUrl/api/agent/api3-enrichment" ` -Method Post ` -Headers $Headers ` -ContentType "application/json" ` -Body $json Write-Host "" Write-Host "Local checked: $checked" Write-Host "API3 OK: $api3Ok" Write-Host "API3 failed: $api3Failed" Write-Host "" $result | ConvertTo-Json -Depth 20