"""Scoped VM100 interactive cursor exporter. No new network listener or system cursor theme.""" import argparse import base64 import json from pathlib import Path import runpy import sys sys.stdout.reconfigure(encoding="utf-8", errors="replace") parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("action", choices=["start", "status", "export", "stop"]) parser.add_argument("--output", type=Path) args = parser.parse_args() root = Path(__file__).resolve().parent t = runpy.run_path(str(root / "pve_term_exec.py")) v = runpy.run_path(str(root / "pve_vm.py")) ticket, csrf = t["login"]() if v["api"]("/api2/json/nodes/starhe/qemu/100/status/current", ticket=ticket).get("status") != "running": sys.exit("VM100 is not running") command = r"$ErrorActionPreference='Stop'; $folder='C:\ProgramData\VDI\cursor-poc-20261103'; $task='VDICursorPOC'; " if args.action == "start": payload = base64.b64encode((root / "cursor-poc-host.ps1").read_bytes()).decode() command += r""" if(Get-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue) { throw 'Probe already exists' } $user=(Get-CimInstance Win32_ComputerSystem).UserName if(-not $user) { throw 'No interactive user' } New-Item -ItemType Directory -Force $folder | Out-Null $acl=Get-Acl $folder $acl.SetAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($user,'Modify','ContainerInherit,ObjectInherit','None','Allow'))) Set-Acl $folder $acl Remove-Item "$folder\error.txt","$folder\state.json","$folder\shapes.json" -ErrorAction SilentlyContinue [IO.File]::WriteAllBytes("$folder\probe.ps1",[Convert]::FromBase64String('__PAYLOAD__')) $a=New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -STA -ExecutionPolicy Bypass -WindowStyle Hidden -File $folder\probe.ps1" $p=New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive -RunLevel Limited $s=New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 9) Register-ScheduledTask -TaskName $task -Action $a -Principal $p -Settings $s | Out-Null Start-ScheduledTask -TaskName $task Start-Sleep -Seconds 3 if(Test-Path "$folder\error.txt") { throw (Get-Content "$folder\error.txt" -Raw) } if(-not(Test-Path "$folder\shapes.json")) { throw 'No fresh cursor export' } 'Cursor probe started' """.replace("__PAYLOAD__", payload) elif args.action == "export": command += 'Get-Content "$folder\\shapes.json" -Raw' elif args.action == "status": command += r""" Get-Content "$folder\state.json" -Raw Get-Content "$folder\clicks.csv" Get-Content "$folder\states.csv" Get-Content "$folder\error.txt" -ErrorAction SilentlyContinue """ else: command += r""" Stop-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue Unregister-ScheduledTask -TaskName $task -Confirm:$false -ErrorAction SilentlyContinue if(Test-Path "$folder\pid.txt") { $probePid=[int](Get-Content "$folder\pid.txt") $process=Get-CimInstance Win32_Process -Filter "ProcessId=$probePid" if($process.CommandLine -like '*cursor-poc-20261103*') { Stop-Process -Id $probePid -ErrorAction SilentlyContinue } } 'Cursor probe stopped' """ out, error = v["agent"](ticket, csrf, ["powershell.exe", "-NoProfile", "-Command", command]) if error or not out or out == "TIMEOUT": sys.exit(error or out or "Empty guest-agent result") if args.action == "export": data = json.loads(out.lstrip("\ufeff")) if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(data, separators=(",", ":")), encoding="utf-8") print(json.dumps([{k: s[k] for k in ("name", "width", "height", "hotX", "hotY", "monochrome", "invertedPixels")} for s in data["shapes"]])) else: print(out)