"""Replace LibreChat's default UI strings with Inference Co-op wording. Run at BUILD time (the runtime rootfs is read-only under Cloudron). Non-fatal: walks the built client assets, replaces exact strings, and deletes the stale precompressed (.gz/.br) siblings of any edited file so the server never serves an old compressed copy. Idempotent: skips files already patched. """ import sys import glob import os REPLACEMENTS = [ # "MCP Servers" button -> "Tools" (more generic / accessible) ("MCP Servers", "Tools"), # LibreChat footer tagline -> co-op footer. custom2.js upgrades this to a # linked version at runtime; this plain text is the no-JS fallback. ("Every AI for Everyone.", "Inference Cooperative · Our AI, governed together"), ] assets_dir = "/app/code/librechat/client/dist/assets" files = glob.glob(os.path.join(assets_dir, "*.js")) + glob.glob( os.path.join(assets_dir, "*.json") ) changed_any = False for path in files: try: with open(path, encoding="utf-8") as f: src = f.read() except (UnicodeDecodeError, OSError): continue new = src applied = [] for old, replacement in REPLACEMENTS: if old in new: new = new.replace(old, replacement) applied.append(old) if not applied: continue with open(path, "w", encoding="utf-8") as f: f.write(new) # Stale precompressed copies of an edited file must go, or the server # may keep serving the old compressed content. for ext in (".gz", ".br"): stale = path + ext if os.path.exists(stale): os.remove(stale) print(f"INFO: patched {os.path.basename(path)}: {applied}") changed_any = True if not changed_any: print("WARN: no UI strings patched (files may already be patched) — non-fatal", file=sys.stderr) sys.exit(0)