open_browser_cdp9000.py
Python script that launches the Antigravity IDE's built-in visible browser by connecting to the IDE's CDP debug port on localhost:9000, clicking the Chrome icon in the workbench, and then waiting for the browser's own CDP port (9222) to become active. Falls back to a Connect-RPC HTTP/1.1 call if the debug port method fails.
import asyncio
from playwright.async_api import async_playwright
import subprocess
import time
import sys
import urllib.request
import ssl
import json
import re
def get_csrf_token_and_port():
try:
# Note: double quotes inside PowerShell must be escaped carefully
cmd = 'powershell -Command "Get-CimInstance Win32_Process -Filter \\"name LIKE \'%language_server_windows_x64%\'\\" | Select-Object ProcessId, CommandLine | ConvertTo-Json"'
output = subprocess.check_output(cmd, shell=True, text=True, encoding='utf-8')
processes = json.loads(output)
proc_list = processes if isinstance(processes, list) else [processes]
# Get connections
port_cmd = 'powershell -Command "Get-NetTCPConnection -State Listen | Where-Object { $_.LocalAddress -eq \'127.0.0.1\' -or $_.LocalAddress -eq \'::1\' } | Select-Object LocalPort, OwningProcess | ConvertTo-Json"'
port_output = subprocess.check_output(port_cmd, shell=True, text=True, encoding='utf-8')
connections = json.loads(port_output)
conn_list = connections if isinstance(connections, list) else [connections]
for proc in proc_list:
if not proc or "ProcessId" not in proc:
continue
pid = proc["ProcessId"]
cmdline = proc.get("CommandLine", "")
# Find ports owned by this pid
ports = [c["LocalPort"] for c in conn_list if c and c.get("OwningProcess") == pid]
csrf_match = re.search(r'--csrf_token\s+([a-fA-F0-9\-]+)', cmdline)
if csrf_match and ports:
# Find port that isn't the extension server (which is 40139 or 31713)
target_port = None
for p in ports:
if p not in [40139, 31713, 9953]:
target_port = p
break
if target_port:
return csrf_match.group(1), target_port
return None, None
except Exception as e:
print(f"Error discovering process: {e}", file=sys.stderr)
return None, None
def launch_browser_via_http1(target_url="about:blank"):
csrf_token, port = get_csrf_token_and_port()
if not csrf_token or not port:
print("Could not find active IDE language server port and CSRF token.", file=sys.stderr)
return False
print(f"Discovered active language server on port {port}", file=sys.stderr)
# Try both http and https protocols
for proto in ["http", "https"]:
url = f"{proto}://127.0.0.1:{port}/exa.language_server_pb.LanguageServerService/SmartOpenBrowser"
headers = {
"Content-Type": "application/json",
"connect-protocol-version": "1",
"x-codeium-csrf-token": csrf_token
}
payload = {
"url": target_url
}
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
try:
print(f"Sending Connect-RPC JSON launch command to {url}...", file=sys.stderr)
with urllib.request.urlopen(req, context=ctx) as response:
if response.status == 200:
print("Programmatic Connect-RPC launch command succeeded!", file=sys.stderr)
return True
except Exception as e:
print(f"Connection failed over {proto}: {e}", file=sys.stderr)
return False
async def launch_browser(target_url="about:blank"):
# Method 4: Connect to Antigravity IDE debug port on port 9000
try:
async with async_playwright() as p:
print("Attempting Method 4: Connecting to Antigravity IDE debug port on http://localhost:9000...", file=sys.stderr)
browser = await p.chromium.connect_over_cdp("http://localhost:9000")
try:
context = browser.contexts[0]
pages = context.pages
workbench_page = None
for page in pages:
if "workbench.html" in page.url:
workbench_page = page
break
if not workbench_page:
print("Could not find IDE workbench page.", file=sys.stderr)
return False
print("Found IDE workbench. Searching for the Chrome icon button...", file=sys.stderr)
button = workbench_page.locator("a.codicon-chrome")
if await button.count() == 0:
print("Could not find the Chrome icon button in the DOM.", file=sys.stderr)
return False
print("Found the button. Clicking it now...", file=sys.stderr)
await button.click()
print("Button clicked successfully!", file=sys.stderr)
return True
finally:
await browser.close()
except Exception as e:
print(f"Method 4 failed: {e}. Falling back to Connect-RPC HTTP/1.1 method...", file=sys.stderr)
# Method 1 Fallback: Call SmartOpenBrowser via Connect-RPC
return launch_browser_via_http1(target_url)
def wait_for_cdp_port(timeout=10):
print("Waiting for browser CDP port 9222 to become active...", file=sys.stderr)
for _ in range(timeout * 2):
try:
# Query if port 9222 is active
cmd = 'powershell -Command "Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -eq 9222 }"'
subprocess.check_output(cmd, shell=True)
print("Browser CDP port 9222 is active and listening!", file=sys.stderr)
return True
except subprocess.CalledProcessError:
time.sleep(0.5)
print("Timeout waiting for port 9222.", file=sys.stderr)
return False
if __name__ == "__main__":
if asyncio.run(launch_browser()):
wait_for_cdp_port()