#!/usr/bin/env python3 """Put Fusion AI (powered by HAL 8) into the hub, replacing the old assistant pane. python3 tools/fusion_inject.py The widget itself is fusion/fusion.js (served as a file, loaded on demand) and fusion/fusion.css (inlined into the bundle). The old block, from its "FUSION AI — assistant pane" banner to the end of the FusionAI component, is replaced by a small component between /*F8-BEGIN*/ and /*F8-END*/; re-running replaces that block and the style block again, so the script is idempotent. The endpoint is HAL 8's public Fusion route; window.FUSION_AI_ENDPOINT still overrides it. """ import hashlib import re import subprocess import sys from pathlib import Path SITE = Path(__file__).resolve().parents[1] ENDPOINT = "https://hal8.odyssey-works.io/v1/public/fusion/chat" tool = SITE / "tools" / "bundle.py" tmp = SITE / "tools" / "_page.html" subprocess.run([sys.executable, str(tool), "decode", str(SITE / "index.html"), str(tmp)], check=True, capture_output=True) page = tmp.read_text("utf-8") VER = hashlib.sha1((SITE / "fusion" / "fusion.js").read_bytes()).hexdigest()[:10] COMPONENT = f"""/*F8-BEGIN*/ // ============================================================================ // FUSION AI — powered by HAL 8 (fusion/fusion.js; the key lives on HAL 8, never here) // ============================================================================ function FusionAI() {{ const ref = useRef(null); useEffect(() => {{ let api = null, dead = false; import("./fusion/fusion.js?v={VER}") .then(m => {{ if (!dead) api = m.mount(ref.current, {{ endpoint: window.FUSION_AI_ENDPOINT || "{ENDPOINT}" }}); }}) .catch(e => console.warn("[Fusion AI]", e)); return () => {{ dead = true; if (api) api.destroy(); }}; }}, []); return MG("div", {{ ref }}); }} /*F8-END*/ """ if "/*F8-BEGIN*/" in page: i = page.index("/*F8-BEGIN*/") j = page.index("/*F8-END*/\n", i) + len("/*F8-END*/\n") else: banner = page.index("// FUSION AI — assistant pane") i = page.rfind("// ====", 0, banner) f = page.index("function FusionAI()", banner) j = re.compile(r"\n}\n").search(page, f).end() old = page[i:j] assert old.count("function ") == 1 and "SYSTEM_PROMPT" in old and "Powered by Claude" in old, "unexpected old block" page = page[:i] + COMPONENT + page[j:] assert "Powered by Claude" not in page and page.count("function FusionAI()") == 1 css = (SITE / "fusion" / "fusion.css").read_text("utf-8") page = re.sub(r"\n", "", page, flags=re.S) k = page.rfind("") page = page[:k] + "\n" + page[k:] tmp.write_text(page, "utf-8") r = subprocess.run([sys.executable, str(tool), "encode", str(SITE / "index.html"), str(tmp), str(SITE / "index.html")], capture_output=True, text=True) tmp.unlink() print(r.stdout.strip() or r.stderr.strip()) print(f"fusion.js v{VER}")