Autosave: 20260319-162804

This commit is contained in:
Flatlogic Bot 2026-03-19 16:28:04 +00:00
parent 817d4c2cca
commit ace24fa9d2

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import base64
import io
import json
import os
import re
@ -8,10 +9,12 @@ import subprocess
import sys
import time
import traceback
import zipfile
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from typing import Optional
from urllib.parse import urlparse
_LOG_LEVELS = {"debug": 10, "info": 20, "error": 40, "quiet": 100}
@ -585,20 +588,172 @@ class Handler(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(body)
def _send_html(self, status: int, html: str):
body = html.encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(body)
def _send_bytes(self, status: int, body: bytes, content_type: str, content_disposition: Optional[str] = None):
self.send_response(status)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(body)))
self.send_header("Access-Control-Allow-Origin", "*")
if content_disposition:
self.send_header("Content-Disposition", content_disposition)
self.end_headers()
self.wfile.write(body)
def _build_extension_zip(self) -> tuple[Optional[bytes], Optional[str]]:
project_root: Path = self.server.project_root # type: ignore[attr-defined]
ext_dir = project_root / "chrome_screenshot_ext"
if not ext_dir.exists() or not ext_dir.is_dir():
return None, f"missing extension directory: {ext_dir}"
buf = io.BytesIO()
with zipfile.ZipFile(buf, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
for p in sorted(ext_dir.rglob("*")):
if p.is_file():
zf.write(p, arcname=p.relative_to(project_root))
return buf.getvalue(), None
def _health_payload(self) -> dict:
return {
"ok": True,
"service": "local_screenshot_bridge",
"out_dir": str(self.server.out_dir), # type: ignore[attr-defined]
"has_run_cmd": bool(getattr(self.server, "run_cmd", None)), # type: ignore[attr-defined]
"ai_enabled": bool(getattr(self.server, "ai_enabled", False)), # type: ignore[attr-defined]
}
def _home_html(self) -> str:
health = self._health_payload()
ai_text = "Enabled" if health["ai_enabled"] else "Disabled"
run_cmd_text = "Configured" if health["has_run_cmd"] else "Not configured"
return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Local Screenshot Bridge</title>
<style>
:root {{
--bg: #f8fafc;
--card: #ffffff;
--text: #0f172a;
--muted: #475569;
--line: #e2e8f0;
--accent: #2563eb;
--good: #16a34a;
}}
* {{ box-sizing: border-box; }}
body {{
margin: 0;
background: linear-gradient(180deg, #f8fafc, #eef2ff);
color: var(--text);
font: 16px/1.5 Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
padding: 36px 16px;
}}
.wrap {{ max-width: 820px; margin: 0 auto; }}
.card {{
background: var(--card);
border: 1px solid var(--line);
border-radius: 16px;
padding: 26px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
}}
h1 {{ margin: 0 0 8px; font-size: 1.8rem; }}
p {{ margin: 0 0 14px; color: var(--muted); }}
.status {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 10px;
margin: 18px 0 8px;
}}
.pill {{
border: 1px solid var(--line);
border-radius: 12px;
padding: 10px 12px;
background: #fff;
}}
.pill strong {{ color: var(--good); }}
ol {{
margin: 12px 0 0;
padding-left: 20px;
}}
code {{
background: #f1f5f9;
border: 1px solid var(--line);
border-radius: 8px;
padding: 2px 6px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: .92em;
}}
a {{ color: var(--accent); text-decoration: none; }}
a:hover {{ text-decoration: underline; }}
</style>
</head>
<body>
<main class="wrap">
<section class="card">
<h1> Screenshot Bridge is running</h1>
<p>This server accepts screenshot payloads from the Chrome extension and can generate AI response suggestions.</p>
<div class="status">
<div class="pill">Server: <strong>Online</strong></div>
<div class="pill">AI: <strong>{ai_text}</strong></div>
<div class="pill">Run command: <strong>{run_cmd_text}</strong></div>
</div>
<h2>How to use</h2>
<ol>
<li>Download the extension zip from <a href="/download/chrome-extension.zip"><code>/download/chrome-extension.zip</code></a> (or copy <code>chrome_screenshot_ext</code> manually).</li>
<li>Open <code>chrome://extensions</code> in Chrome and enable <strong>Developer mode</strong>.</li>
<li>Click <strong>Load unpacked</strong> and choose the <code>chrome_screenshot_ext</code> folder.</li>
<li>Open any page, run the extension, and send a screenshot to this server.</li>
</ol>
<p style="margin-top:16px;">Need JSON health output? Use <a href="/health"><code>/health</code></a>.</p>
</section>
</main>
</body>
</html>"""
def do_GET(self): # noqa: N802
if self.path not in ("/", "/health"):
self._send_json(404, {"ok": False, "error": "not_found"})
parsed = urlparse(self.path)
path = parsed.path or "/"
if path == "/":
# Keep root JSON-compatible for extension and existing integrations.
self._send_json(200, self._health_payload())
return
self._send_json(
200,
{
"ok": True,
"service": "local_screenshot_bridge",
"out_dir": str(self.server.out_dir), # type: ignore[attr-defined]
"has_run_cmd": bool(getattr(self.server, "run_cmd", None)), # type: ignore[attr-defined]
"ai_enabled": bool(getattr(self.server, "ai_enabled", False)), # type: ignore[attr-defined]
},
)
if path in ("/guide", "/docs"):
self._send_html(200, self._home_html())
return
if path in ("/download/chrome-extension.zip", "/chrome_screenshot_ext.zip"):
zip_bytes, err = self._build_extension_zip()
if err:
_log(self.server, "error", f"Extension zip failed: {err}") # type: ignore[arg-type]
self._send_json(500, {"ok": False, "error": "extension_zip_failed", "detail": err})
return
self._send_bytes(
200,
zip_bytes or b"",
"application/zip",
'attachment; filename="chrome_screenshot_ext.zip"',
)
return
if path == "/health":
self._send_json(200, self._health_payload())
return
self._send_json(404, {"ok": False, "error": "not_found"})
def do_OPTIONS(self): # noqa: N802
self.send_response(204)