#!/usr/bin/env python3
"""Real end-to-end dogfood: start a throwaway dev server, detect it with
sidecar.py watch (no browser opened), then capture a real screenshot receipt
with Chromium headless. No mocks. Cleans up only its own temp resources.
The throwaway server runs as an in-process thread rather than a `python -m
http.server` subprocess: this project is itself built and tested inside
Claude Code's own sandboxed Bash tool, which puts a fresh subprocess's
listening socket in a network namespace its own parent can't reach -- a
sandbox artifact of *this* harness, not of real terminals. A thread-based
listener sidesteps that so the demo can prove the real detect -> preview ->
screenshot path with an actual TCP probe from a separate process and an
actual headless-Chrome screenshot. On a normal dev machine, `npm run dev` /
`vite` / etc. are plain reachable subprocesses and this workaround is moot.
"""
import http.server
import json
import shutil
import subprocess
import sys
import tempfile
import threading
from pathlib import Path
ROOT = Path(__file__).parent.resolve()
SIDECAR = ROOT / "sidecar.py"
class _QuietHandler(http.server.SimpleHTTPRequestHandler):
def log_message(self, *args):
pass
def start_demo_server(site_dir):
handler = lambda *a, **kw: _QuietHandler(*a, directory=site_dir, **kw)
httpd = http.server.HTTPServer(("127.0.0.1", 0), handler)
thread = threading.Thread(target=httpd.serve_forever, daemon=True)
thread.start()
return httpd
def main():
with tempfile.TemporaryDirectory(prefix="preview-sidecar-demo-") as site_dir, \
tempfile.TemporaryDirectory(prefix="preview-sidecar-out-") as out_dir:
(Path(site_dir) / "index.html").write_text(
"
"
""
"
Hello from dev server
"
"
Started by preview-sidecar demo.py
"
"
")
httpd = start_demo_server(site_dir)
port = httpd.server_port
print(f"$ (throwaway dev server listening on 127.0.0.1:{port}, serving a one-page site)")
try:
print(f"$ python3 sidecar.py watch --ports {port} --interval 0.3 --max-cycles 3 --no-open --out {out_dir}")
watch = subprocess.run(
[sys.executable, str(SIDECAR), "watch", "--ports", str(port),
"--interval", "0.3", "--max-cycles", "3", "--no-open", "--out", out_dir],
capture_output=True, text=True, timeout=30)
print(watch.stdout.strip())
assert f"detected 127.0.0.1:{port}" in watch.stdout, watch.stdout
events = (Path(out_dir) / "events.jsonl").read_text().strip().splitlines()
assert len(events) == 1, events
event = json.loads(events[0])
assert event["port"] == port and event["event"] == "detected", event
preview_path = Path(event["preview"])
assert preview_path.exists()
assert f"http://127.0.0.1:{port}" in preview_path.read_text()
print(f" preview pane written: {preview_path.name} (iframe -> http://127.0.0.1:{port})")
chrome = shutil.which("google-chrome") or shutil.which("chromium") or \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
print(f"$ python3 sidecar.py screenshot --port {port} --out {out_dir} --chrome '{chrome}'")
shot = subprocess.run(
[sys.executable, str(SIDECAR), "screenshot", "--port", str(port),
"--out", out_dir, "--chrome", chrome],
capture_output=True, text=True, timeout=30)
print(shot.stdout.strip() or shot.stderr.strip())
if shot.returncode == 0:
receipt = json.loads(shot.stdout)
assert Path(receipt["png"]).stat().st_size > 0
print(f" receipt verified: {receipt['bytes']} bytes, sha256={receipt['sha256'][:12]}...")
else:
print(" (no Chromium-family browser on this host; screenshot step skipped honestly)")
finally:
httpd.shutdown()
httpd.server_close()
print(f"$ (dev server on {port} stopped)")
if __name__ == "__main__":
main()