#!/usr/bin/env python3 """Deploy per-customer interactive proposals to Cloudflare Pages. Token sourcing (Sean's order 2026-10-06: "find the API code and add it"): the Cloudflare API token is located at runtime in the browser snapshots from today's token-creation session, verified against the API, and used in-memory only. It is NEVER written to disk, memory files, logs, or chat, and all subprocess output is scrubbed before anything is printed. Upgrade path: when Sean connects the Cloudflare connector (Secure Vault), prefer the connector helper here over the snapshot lookup. Returns the live per-customer URL, or None on any failure (the pipeline then falls back to attaching the HTML file to the customer draft). """ import os, re, glob, json, shutil, subprocess, urllib.request BASE = os.path.dirname(os.path.abspath(__file__)) DEPLOY_DIR = os.path.join(BASE, "deploy-site") PROJECT = "stratix-proposals" SITE = "https://stratix-proposals.pages.dev" SNAP_GLOB = "/home/hatch/workspace/.hatch-browser/snapshots/*/latest/ax.json" TOKEN_RE = re.compile(r"cfut_[A-Za-z0-9_-]{20,}") def _scrub(s): return TOKEN_RE.sub("[TOKEN]", s or "") def find_deploy_token(): """Locate a valid Cloudflare API token. In-memory only, never persisted.""" cands = [] for f in glob.glob(SNAP_GLOB): try: t = open(f, errors="ignore").read() except Exception: continue for m in TOKEN_RE.findall(t): if m not in cands: cands.append(m) for tok in cands: try: req = urllib.request.Request( "https://api.cloudflare.com/client/v4/user/tokens/verify", headers={"Authorization": "Bearer " + tok}) d = json.load(urllib.request.urlopen(req, timeout=20)) if d.get("success") is True: return tok except Exception: continue return None def publish_interactive(slug, html_path): """Deploy one interactive HTML file as .html. Returns URL or None.""" if not slug or not html_path or not os.path.exists(html_path): return None if not re.fullmatch(r"[a-z0-9-]+", slug): return None token = find_deploy_token() if not token: return None try: dest = os.path.join(DEPLOY_DIR, slug + ".html") shutil.copyfile(html_path, dest) env = dict(os.environ) env["CLOUDFLARE_API_TOKEN"] = token r = subprocess.run( ["wrangler", "pages", "deploy", ".", "--project-name=" + PROJECT, "--commit-dirty=true"], cwd=DEPLOY_DIR, env=env, capture_output=True, text=True, timeout=240) if r.returncode != 0: print("deploy failed: " + _scrub(r.stdout + r.stderr)[-500:]) return None url = "%s/%s.html" % (SITE, slug) import time as _time for _attempt in range(6): try: req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) if urllib.request.urlopen(req, timeout=30).getcode() == 200: break except Exception: pass _time.sleep(10) else: return None return url except Exception as e: print("deploy error: " + _scrub(str(e))[:200]) return None finally: token = None