111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
"""
|
|
Live screenshot capture for Hermes Hub Web Client (A18).
|
|
Captures model selection flow (before, selection modal, account details, and after) via Headless Chrome CLI.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import http.server
|
|
import os
|
|
from pathlib import Path
|
|
import socket
|
|
import subprocess
|
|
import threading
|
|
import time
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
STATIC_DIR = REPO_ROOT / "src" / "antigravity_provider" / "router" / "web" / "static"
|
|
ARTIFACTS_DIR = REPO_ROOT / "artifacts" / "a18-screenshots"
|
|
ARTIFACTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
CHROME_PATHS = [
|
|
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
|
|
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
|
r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
|
|
r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
|
|
]
|
|
|
|
|
|
class StaticServer(threading.Thread):
|
|
def __init__(self, port: int):
|
|
super().__init__(daemon=True)
|
|
self.port = port
|
|
self.httpd = None
|
|
|
|
def run(self):
|
|
class Handler(http.server.SimpleHTTPRequestHandler):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, directory=str(STATIC_DIR), **kwargs)
|
|
|
|
def log_message(self, *args):
|
|
pass
|
|
|
|
self.httpd = http.server.HTTPServer(("127.0.0.1", self.port), Handler)
|
|
self.httpd.serve_forever()
|
|
|
|
def stop(self):
|
|
if self.httpd:
|
|
self.httpd.shutdown()
|
|
|
|
|
|
def get_free_port() -> int:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
s.bind(("127.0.0.1", 0))
|
|
return s.getsockname()[1]
|
|
|
|
|
|
def main():
|
|
port = get_free_port()
|
|
server = StaticServer(port)
|
|
server.start()
|
|
print(f"Static HTTP Server running on http://127.0.0.1:{port}")
|
|
time.sleep(0.3)
|
|
|
|
chrome_exe = None
|
|
for p in CHROME_PATHS:
|
|
if os.path.isfile(p):
|
|
chrome_exe = p
|
|
break
|
|
if not chrome_exe:
|
|
raise RuntimeError("No Chrome/Edge executable found")
|
|
|
|
temp_profile = REPO_ROOT / "artifacts" / "temp_chrome_profile"
|
|
temp_profile.mkdir(parents=True, exist_ok=True)
|
|
|
|
scenarios = [
|
|
("01_model_choice_team_before.png", f"http://127.0.0.1:{port}/index.html?view=team"),
|
|
("02_model_choice_modal.png", f"http://127.0.0.1:{port}/index.html?modal=agent_model&role=coder-primary"),
|
|
("03_model_choice_account_details.png", f"http://127.0.0.1:{port}/index.html?modal=account_details&profile=ag-w1"),
|
|
("04_web_accounts_view.png", f"http://127.0.0.1:{port}/index.html?view=accounts"),
|
|
("05_web_providers_discovered_models.png", f"http://127.0.0.1:{port}/index.html?view=providers"),
|
|
]
|
|
|
|
captured_count = 0
|
|
for filename, url in scenarios:
|
|
out_file = ARTIFACTS_DIR / filename
|
|
cmd = [
|
|
chrome_exe,
|
|
"--headless=new",
|
|
"--disable-gpu",
|
|
"--no-sandbox",
|
|
"--hide-scrollbars",
|
|
"--virtual-time-budget=2000",
|
|
f"--user-data-dir={temp_profile}",
|
|
"--window-size=1440,920",
|
|
f"--screenshot={out_file}",
|
|
url,
|
|
]
|
|
res = subprocess.run(cmd, capture_output=True, timeout=20)
|
|
if res.returncode == 0 and out_file.exists():
|
|
size = out_file.stat().st_size
|
|
print(f"Captured: {filename} ({size} bytes)")
|
|
captured_count += 1
|
|
else:
|
|
print(f"FAILED to capture {filename}: returncode {res.returncode}")
|
|
|
|
server.stop()
|
|
print(f"\nTotal screenshots captured: {captured_count}/{len(scenarios)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|