Files
Sepehr ab5dc7e568 chore: remove BMAD framework files and IDE configuration artifacts
Clean up unused BMAD workflow, agent, and command files across all IDE
configurations (.agent, .clinerules, .cursor, .gemini, .github, .kilocode,
.opencode) and internal module files (_bmad/bmb, _bmad/bmm).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:09 +02:00

132 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
Minimal backend for Circuit Builder UI.
Serves the static UI and runs entropyk-cli validate/run on POST.
Run from repository root: python tools/circuit-builder-ui/server.py
Then open http://localhost:8765
"""
import json
import os
import subprocess
import tempfile
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
from urllib.parse import parse_qs, urlparse
PORT = 8765
# Path to entropyk-cli from repo root (run server from repo root)
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
CLI_PATH = REPO_ROOT / "target" / "release" / "entropyk-cli"
UI_DIR = Path(__file__).resolve().parent
def run_cli(subcommand, config_json_str, run_output=None):
"""Run entropyk-cli validate or run; config_json_str written to temp file."""
if not CLI_PATH.exists():
return False, f"CLI not found. Build first: cargo build --release -p entropyk-cli\nExpected: {CLI_PATH}", ""
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
f.write(config_json_str)
tmp_path = f.name
try:
cmd = [str(CLI_PATH), subcommand, "--config", tmp_path]
if run_output is not None:
cmd.extend(["-o", run_output])
r = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=60,
cwd=str(REPO_ROOT),
)
stdout = r.stdout or ""
stderr = r.stderr or ""
if r.returncode != 0:
return False, stdout or stderr or f"Exit code {r.returncode}", stderr
return True, stdout, stderr
except subprocess.TimeoutExpired:
return False, "Timeout (60s)", ""
except Exception as e:
return False, str(e), ""
finally:
try:
os.unlink(tmp_path)
except Exception:
pass
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
path = urlparse(self.path).path
if path == "/" or path == "/index.html":
self.serve_file(UI_DIR / "index.html", "text/html")
elif path == "/examples/simple_working.json":
self.serve_file(REPO_ROOT / "crates" / "cli" / "examples" / "simple_working.json", "application/json")
elif path == "/examples/chiller_r410a_minimal.json":
self.serve_file(REPO_ROOT / "crates" / "cli" / "examples" / "chiller_r410a_minimal.json", "application/json")
else:
self.send_error(404)
def serve_file(self, filepath, content_type):
try:
with open(filepath, "rb") as f:
data = f.read()
except FileNotFoundError:
self.send_error(404)
return
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_POST(self):
path = urlparse(self.path).path
if path != "/validate" and path != "/run":
self.send_error(404)
return
content_len = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_len)
try:
config_str = body.decode("utf-8")
json.loads(config_str)
except Exception as e:
self.send_json(400, {"ok": False, "error": f"Invalid JSON: {e}"})
return
if path == "/validate":
ok, out, err = run_cli("validate", config_str)
else:
ok, out, err = run_cli("run", config_str, run_output=os.path.devnull)
self.send_json(200, {"ok": ok, "stdout": out, "stderr": err})
def send_json(self, status, obj):
body = json.dumps(obj).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, format, *args):
print(f"[{self.log_date_time_string()}] {format % args}")
def main():
os.chdir(REPO_ROOT)
print(f"Entropyk Circuit Builder UI")
print(f" UI: http://localhost:{PORT}/")
print(f" CLI: {CLI_PATH} {'(found)' if CLI_PATH.exists() else '(not found — run: cargo build --release -p entropyk-cli)'}")
print(f" Root: {REPO_ROOT}")
server = HTTPServer(("", PORT), Handler)
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nBye.")
server.server_close()
if __name__ == "__main__":
main()