#!/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}")