#!/usr/bin/env python3 """Recon UX backend: write files, test, commit, push.""" from pathlib import Path import subprocess, sys ROOT = Path(__file__).resolve().parents[1] S = ROOT / "server" def w(rel, text): p = S / rel p.parent.mkdir(parents=True, exist_ok=True) p.write_text(text, encoding="utf-8", newline="\n") print("wrote", rel) w("internal/recon/types.go", Path(__file__).with_name("types.go").read_text(encoding="utf-8")) w("internal/recon/recon_ux.go", Path(__file__).with_name("recon_ux.go").read_text(encoding="utf-8")) w("internal/recon/scan.go", Path(__file__).with_name("scan.go").read_text(encoding="utf-8")) w("internal/db/recon_scans.go", Path(__file__).with_name("recon_scans.go").read_text(encoding="utf-8")) w("internal/api/recon_handler.go", Path(__file__).with_name("recon_handler.go").read_text(encoding="utf-8")) w("internal/api/recon_handler_test.go", Path(__file__).with_name("recon_handler_test.go").read_text(encoding="utf-8")) w("internal/recon/recon_stream_test.go", Path(__file__).with_name("recon_stream_test.go").read_text(encoding="utf-8")) # crawl delegate crawl = (S / "internal/recon/crawl.go").read_text(encoding="utf-8") if "crawlWithOptions" not in crawl.split("func Crawl(")[1].split("\nfunc ")[0]: crawl = crawl.replace( "func Crawl(host string, port int, scheme string, seedPaths []string) (*CrawlReport, error) {\n\tscheme = normalizeScheme", "func Crawl(host string, port int, scheme string, seedPaths []string) (*CrawlReport, error) {\n\treturn crawlWithOptions(host, port, scheme, seedPaths, ProfileOptions(\"\"), \"\", nil)\n}\n\nfunc crawlLegacy(host string, port int, scheme string, seedPaths []string) (*CrawlReport, error) {\n\tscheme = normalizeScheme", 1, ) w("internal/recon/crawl.go", crawl) sqlite = (S / "internal/db/sqlite.go").read_text(encoding="utf-8") if "ensureReconScansTable" not in sqlite: sqlite = sqlite.replace( "\tif err := d.ensureSeerTables(); err != nil {\n\t\treturn fmt.Errorf(\"seer migration: %w\", err)\n\t}\n", "\tif err := d.ensureSeerTables(); err != nil {\n\t\treturn fmt.Errorf(\"seer migration: %w\", err)\n\t}\n\tif err := d.ensureReconScansTable(); err != nil {\n\t\treturn fmt.Errorf(\"recon_scans migration: %w\", err)\n\t}\n", 1, ) w("internal/db/sqlite.go", sqlite) router = (S / "internal/api/router.go").read_text(encoding="utf-8") if "/recon/history" not in router: router = router.replace( "\t\tr.Post(\"/recon/scan\", reconHandler.Scan)\n", "\t\tr.Post(\"/recon/scan\", reconHandler.Scan)\n\t\tr.Get(\"/recon/history\", reconHandler.History)\n\t\tr.Get(\"/recon/export/{scan_id}\", reconHandler.Export)\n", 1, ) w("internal/api/router.go", router) # fix NewReconHandler call if 3-arg router = (S / "internal/api/router.go").read_text(encoding="utf-8") if "NewReconHandler(database, wsHub, publicURLOverride)" in router: router = router.replace("NewReconHandler(database, wsHub, publicURLOverride)", "NewReconHandler(database, wsHub)") w("internal/api/router.go", router) # websocket test append wt = S / "internal/api/websocket_test.go" body = wt.read_text(encoding="utf-8") if "TestWSReconStreamingEventTypes" not in body: if '"bytes"' not in body.split("import (")[1].split(")")[0]: body = body.replace('"encoding/base64"', '"bytes"\n\t"encoding/base64"', 1) if '"crypto-miner-server/internal/recon"' not in body: body = body.replace('"crypto-miner-server/internal/pool"', '"crypto-miner-server/internal/pool"\n\t"crypto-miner-server/internal/recon"', 1) body = body.rstrip() + "\n\n" + Path(__file__).with_name("websocket_test_snippet.go").read_text(encoding="utf-8").split("package api\n", 1)[1] w("internal/api/websocket_test.go", body) env = {**dict(os.environ), "GOCACHE": str(ROOT / ".gocache")} if False else None r = subprocess.run( ["go", "test", "./internal/recon/...", "./internal/api/...", "-count=1", "-run", "Recon|WSRecon|ScanStream|DiffReports|ProfileOptions|BuildHistory"], cwd=S, ) if r.returncode != 0: sys.exit(r.returncode) subprocess.run(["git", "pull", "origin", "main"], cwd=ROOT, check=False) subprocess.run(["git", "add", "-A"], cwd=ROOT, check=True) subprocess.run(["git", "commit", "-m", "Add recon UX backend: streaming scans, profiles, history, export."], cwd=ROOT, check=True) subprocess.run(["git", "push", "origin", "main"], cwd=ROOT, check=True) print("COMMIT_HASH=" + subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip())