#!/usr/bin/env python3
"""Apply fail-closed Atlas voice integration patches to pinned Hermes WebUI."""
import os
from pathlib import Path
ROOT = Path(os.environ.get("HERMES_WEBUI_PATCH_ROOT", "/opt/hermes-webui"))
def replace_exact(path: Path, before: str, after: str, count: int = 1) -> None:
"""Replace an exact upstream fragment and fail when the pin has drifted."""
source = path.read_text(encoding="utf-8")
if source.count(before) != count:
raise SystemExit(f"Atlas voice patch context changed in {path}: {before[:80]!r}")
path.write_text(source.replace(before, after, count), encoding="utf-8")
def replace_between_exact(
path: Path, start: str, end: str, after: str = "", count: int = 1
) -> None:
"""Replace one exact, bounded upstream region and fail when the pin drifts."""
source = path.read_text(encoding="utf-8")
if source.count(start) != count or source.count(end) != count:
raise SystemExit(
f"Atlas voice patch context changed in {path}: {start[:80]!r}"
)
start_index = source.index(start)
end_index = source.index(end, start_index) + len(end)
path.write_text(
source[:start_index] + after + source[end_index:], encoding="utf-8"
)
def assert_absent(path: Path, *needles: str) -> None:
"""Fail the image build if a removed voice-choice surface remains."""
source = path.read_text(encoding="utf-8")
remaining = [needle for needle in needles if needle in source]
if remaining:
raise SystemExit(f"Atlas voice choice remains in {path}: {remaining!r}")
def remove_lines_containing(path: Path, *needles: str) -> None:
"""Remove all pinned translation entries for a retired settings control."""
source = path.read_text(encoding="utf-8")
for needle in needles:
if needle not in source:
raise SystemExit(f"Atlas voice patch context changed in {path}: {needle!r}")
lines = source.splitlines(keepends=True)
path.write_text(
"".join(line for line in lines if not any(n in line for n in needles)),
encoding="utf-8",
)
index = ROOT / "static/index.html"
replace_exact(
index,
'',
'\n'
'',
)
replace_exact(
index,
'',
'',
)
replace_exact(
index,
'''
Preferred voice. Populated from your browser's available voices.