# Small helper for driving the agent: enqueues one command and waits for the ACK. # # powershell -File agent-cli.ps1 -Command "STATUS" # powershell -File agent-cli.ps1 -Status # powershell -File agent-cli.ps1 -Command "PS Get-Process | Select -First 3" # # Uses the same HMAC scheme the agent does, so it works against the mock worker # and the real Cloudflare Worker alike. param( [string]$Worker = "http://127.0.0.1:8788", [string]$Agent = "testagent", [string]$Secret = "testsecret", [string]$Command = "", [switch]$Status, [int]$TimeoutSec = 30 ) $ErrorActionPreference = "Stop" function Get-Sha256Hex([byte[]]$Bytes) { $sha = [System.Security.Cryptography.SHA256]::Create() try { if ($null -eq $Bytes -or $Bytes.Length -eq 0) { $stream = New-Object System.IO.MemoryStream } else { $stream = New-Object System.IO.MemoryStream(, $Bytes) } return ([BitConverter]::ToString($sha.ComputeHash($stream)) -replace "-", "").ToLower() } finally { $sha.Dispose() } } function Get-HmacHex([string]$Key, [string]$Message) { $hmac = New-Object System.Security.Cryptography.HMACSHA256 try { $hmac.Key = [System.Text.Encoding]::UTF8.GetBytes($Key) return ([BitConverter]::ToString($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Message))) -replace "-", "").ToLower() } finally { $hmac.Dispose() } } function Invoke-Worker([string]$Path, [string]$Method = "GET", [byte[]]$Body) { if ($null -eq $Body) { $Body = [byte[]]@() } $ts = [string][DateTimeOffset]::UtcNow.ToUnixTimeSeconds() $canonical = "$Agent`n$ts`n$Method`n$($Path.TrimStart('/'))`n$(Get-Sha256Hex $Body)" $headers = @{ "X-Agent-Id" = $Agent "X-Agent-Ts" = $ts "X-Agent-Sign" = (Get-HmacHex $Secret $canonical) } if ($Method -eq "POST") { return Invoke-WebRequest -Method Post -Uri "$Worker$Path" -Body ([byte[]]$Body) ` -ContentType "application/octet-stream" -Headers $headers -UseBasicParsing } return Invoke-WebRequest -Uri "$Worker$Path" -Headers $headers -UseBasicParsing } function Decode-Last([string]$Value) { if (-not $Value) { return "" } try { return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value)) } catch { return $Value } } if ($Status) { $s = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json "agent=$($s.agent) queued=$($s.queued) pending=$([bool]$s.pending)" if ($s.last) { Decode-Last $s.last } exit 0 } if (-not $Command) { throw "Usage: agent-cli.ps1 -Command '' | -Status" } $before = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json $initialLast = $before.last $null = Invoke-Worker -Path "/enqueue" -Method POST -Body ([System.Text.Encoding]::UTF8.GetBytes($Command)) $deadline = (Get-Date).AddSeconds($TimeoutSec) $seenPending = $false while ((Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 400 $s = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json if ($s.pending) { $seenPending = $true; continue } if ($s.last -and $s.last -ne $initialLast) { Decode-Last $s.last; exit 0 } if ($seenPending -and $s.last) { Decode-Last $s.last; exit 0 } } Write-Host "" exit 1