219 lines
8.0 KiB
PowerShell
219 lines
8.0 KiB
PowerShell
param(
|
|
[string]$HelperExe = "runs/win-helper/ISphereWinHelper.exe",
|
|
[switch]$SkipBuild
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$repo = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")
|
|
$helperPath = Join-Path $repo $HelperExe
|
|
|
|
function Invoke-HelperJson([hashtable]$Request) {
|
|
$json = $Request | ConvertTo-Json -Depth 8 -Compress
|
|
$output = $json | & $helperPath --json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "helper exited with code $LASTEXITCODE"
|
|
}
|
|
try {
|
|
return $output | ConvertFrom-Json
|
|
}
|
|
catch {
|
|
throw "helper output was not JSON: $output"
|
|
}
|
|
}
|
|
|
|
if (-not $SkipBuild) {
|
|
& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repo "scripts\build-win-helper.ps1") | Out-Host
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "build-win-helper.ps1 failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $helperPath)) {
|
|
throw "helper not found: $helperPath"
|
|
}
|
|
|
|
$version = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-version"
|
|
op = "version"
|
|
timeout_ms = 5000
|
|
args = @{}
|
|
}
|
|
if (-not $version.ok -or $version.data.helper_name -ne "ISphereWinHelper") {
|
|
throw "version check failed: $($version | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$selfCheck = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-self-check"
|
|
op = "self_check"
|
|
timeout_ms = 5000
|
|
args = @{}
|
|
}
|
|
if (-not $selfCheck.ok) {
|
|
throw "self_check failed: $($selfCheck | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$scan = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-scan"
|
|
op = "scan_windows"
|
|
timeout_ms = 5000
|
|
args = @{ include_all_visible = $true }
|
|
}
|
|
if (-not $scan.ok -or $null -eq $scan.data.windows) {
|
|
throw "scan_windows failed: $($scan | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$runtimeProbe = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-runtime-probe"
|
|
op = "probe_client_runtime"
|
|
timeout_ms = 5000
|
|
args = @{
|
|
process_names = @("IMPlatformClient", "IMPP.ISphere", "iSphere", "importal")
|
|
include_modules = $false
|
|
max_modules = 0
|
|
}
|
|
}
|
|
if (-not $runtimeProbe.ok -or $runtimeProbe.data.probe_mode -ne "read_only_process_module_inventory" -or $null -eq $runtimeProbe.data.targets) {
|
|
throw "probe_client_runtime failed: $($runtimeProbe | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$entrypointProbe = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-send-entrypoints"
|
|
op = "probe_send_entrypoints"
|
|
timeout_ms = 5000
|
|
args = @{}
|
|
}
|
|
if (-not $entrypointProbe.ok -or $entrypointProbe.data.probe_mode -ne "read_only_send_entrypoint_metadata" -or $null -eq $entrypointProbe.data.entrypoints) {
|
|
throw "probe_send_entrypoints failed: $($entrypointProbe | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
if ($entrypointProbe.data.safety.sent_message -or $entrypointProbe.data.safety.uploaded_file -or $entrypointProbe.data.safety.clicked_ui) {
|
|
throw "probe_send_entrypoints safety flags failed: $($entrypointProbe | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$dumpMissing = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-dump-missing"
|
|
op = "dump_uia"
|
|
timeout_ms = 5000
|
|
args = @{ hwnd = "0x0"; max_depth = 1; include_text = $true; max_children = 20 }
|
|
}
|
|
if ($dumpMissing.ok -or $dumpMissing.error.code -notin @("WINDOW_NOT_FOUND", "UIA_DUMP_FAILED")) {
|
|
throw "dump_uia missing-window check failed: $($dumpMissing | ConvertTo-Json -Depth 8 -Compress)"
|
|
}
|
|
|
|
$title = "Codex WinHelper Verify " + [guid]::NewGuid().ToString("N").Substring(0, 8)
|
|
$formScriptPath = Join-Path $env:TEMP ("codex-winhelper-form-" + [guid]::NewGuid().ToString("N") + ".ps1")
|
|
$formScript = @"
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
`$form = New-Object System.Windows.Forms.Form
|
|
`$form.Text = '$title'
|
|
`$form.Width = 620
|
|
`$form.Height = 420
|
|
`$layout = New-Object System.Windows.Forms.TableLayoutPanel
|
|
`$layout.Dock = 'Fill'
|
|
`$layout.RowCount = 6
|
|
`$layout.ColumnCount = 1
|
|
`$form.Controls.Add(`$layout)
|
|
`$search = New-Object System.Windows.Forms.TextBox
|
|
`$search.Name = 'skinAlphaTxt'
|
|
`$search.Text = 'search placeholder'
|
|
`$search.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$search)
|
|
`$recv = New-Object System.Windows.Forms.RichTextBox
|
|
`$recv.Name = 'rtbRecvMessage'
|
|
`$recv.Text = ''
|
|
`$recv.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$recv)
|
|
`$send = New-Object System.Windows.Forms.RichTextBox
|
|
`$send.Name = 'rtbSendMessage'
|
|
`$send.Text = ''
|
|
`$send.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$send)
|
|
`$button = New-Object System.Windows.Forms.Button
|
|
`$button.Name = 'btnSend'
|
|
`$button.Text = 'Send'
|
|
`$button.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$button)
|
|
`$file = New-Object System.Windows.Forms.Button
|
|
`$file.Name = 'btnFile'
|
|
`$file.Text = 'File'
|
|
`$file.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$file)
|
|
`$offline = New-Object System.Windows.Forms.Label
|
|
`$offline.Name = 'offlineSendBlocker'
|
|
`$offline.Text = 'Offline send blocker'
|
|
`$offline.Dock = 'Fill'
|
|
`$layout.Controls.Add(`$offline)
|
|
[void]`$form.ShowDialog()
|
|
"@
|
|
Set-Content -LiteralPath $formScriptPath -Value $formScript -Encoding UTF8
|
|
$process = Start-Process powershell -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $formScriptPath) -PassThru
|
|
try {
|
|
$candidate = $null
|
|
for ($i = 0; $i -lt 20 -and -not $candidate; $i++) {
|
|
Start-Sleep -Milliseconds 500
|
|
$scanForForm = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-form-scan"
|
|
op = "scan_windows"
|
|
timeout_ms = 5000
|
|
args = @{ include_all_visible = $true }
|
|
}
|
|
$candidate = $scanForForm.data.windows | Where-Object { $_.title -eq $title } | Select-Object -First 1
|
|
}
|
|
if (-not $candidate) {
|
|
throw "verification form window not found"
|
|
}
|
|
|
|
$dump = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-form-dump"
|
|
op = "dump_uia"
|
|
timeout_ms = 5000
|
|
args = @{ hwnd = $candidate.hwnd; max_depth = 4; include_text = $true; max_children = 80 }
|
|
}
|
|
if (-not $dump.ok -or $dump.data.root.control_type -ne "Window") {
|
|
throw "dump_uia form check failed: $($dump | ConvertTo-Json -Depth 12 -Compress)"
|
|
}
|
|
|
|
$sendUiaProbe = Invoke-HelperJson @{
|
|
protocol = "isphere.helper.v1"
|
|
request_id = "verify-send-uia-controls"
|
|
op = "probe_send_uia_controls"
|
|
timeout_ms = 5000
|
|
args = @{ hwnd = $candidate.hwnd; max_depth = 6; max_children = 120 }
|
|
}
|
|
if (-not $sendUiaProbe.ok -or $sendUiaProbe.data.probe_mode -ne "read_only_uia_send_control_classifier") {
|
|
throw "probe_send_uia_controls failed: $($sendUiaProbe | ConvertTo-Json -Depth 12 -Compress)"
|
|
}
|
|
if (-not $sendUiaProbe.data.flags.has_search_edit -or -not $sendUiaProbe.data.flags.has_send_editor -or -not $sendUiaProbe.data.flags.has_send_button -or -not $sendUiaProbe.data.flags.has_file_menu -or -not $sendUiaProbe.data.flags.offline_blocker_visible) {
|
|
throw "probe_send_uia_controls missing expected synthetic controls: $($sendUiaProbe | ConvertTo-Json -Depth 12 -Compress)"
|
|
}
|
|
if ($sendUiaProbe.data.safety.sent_message -or $sendUiaProbe.data.safety.uploaded_file -or $sendUiaProbe.data.safety.clicked_ui -or $sendUiaProbe.data.safety.typed_text) {
|
|
throw "probe_send_uia_controls safety flags failed: $($sendUiaProbe | ConvertTo-Json -Depth 12 -Compress)"
|
|
}
|
|
}
|
|
finally {
|
|
if ($process -and -not $process.HasExited) {
|
|
Stop-Process -Id $process.Id -Force
|
|
}
|
|
if (Test-Path -LiteralPath $formScriptPath) {
|
|
Remove-Item -LiteralPath $formScriptPath -Force
|
|
}
|
|
}
|
|
|
|
[ordered]@{
|
|
ok = $true
|
|
helper = (Resolve-Path -LiteralPath $helperPath).Path
|
|
version = $version.data.helper_version
|
|
scan_window_count = $scan.data.windows.Count
|
|
runtime_probe_target_count = $runtimeProbe.data.target_count
|
|
send_entrypoint_probe_mode = $entrypointProbe.data.probe_mode
|
|
synthetic_send_uia_route_hint = $sendUiaProbe.data.flags.route_hint
|
|
uia_available = $selfCheck.data.uia_available
|
|
} | ConvertTo-Json -Depth 4 -Compress
|