#!/usr/bin/env python3 """ Hugging Face Space launcher for gemini-web2api. Builds a runtime config.json from the committed config.example.json, applying overrides from Space Secrets / environment variables, then starts the server. Supported environment variables (all optional): PORT port to listen on (default 7860) HOST bind address (default 0.0.0.0) COOKIE raw Gemini cookie string (plain "k=v; k=v" or JSON) -> written to cookie.txt and wired into config API_KEY single API key for Bearer auth (default sk-gemini) API_KEYS comma-separated list of API keys (overrides API_KEY) DEFAULT_MODEL default model name GEMINI_BL Gemini backend version string PROXY HTTP/HTTPS proxy for outbound calls """ import json import os import sys APP_DIR = os.path.dirname(os.path.abspath(__file__)) def main(): # 1. base config from the committed example base_path = os.path.join(APP_DIR, "config.example.json") with open(base_path) as f: cfg = json.load(f) # 2. apply env overrides cfg["port"] = int(os.environ.get("PORT", cfg.get("port", 7860))) cfg["host"] = os.environ.get("HOST", cfg.get("host", "0.0.0.0")) if os.environ.get("DEFAULT_MODEL"): cfg["default_model"] = os.environ["DEFAULT_MODEL"] if os.environ.get("GEMINI_BL"): cfg["gemini_bl"] = os.environ["GEMINI_BL"] if os.environ.get("PROXY"): cfg["proxy"] = os.environ["PROXY"] # 3. API keys (Bearer auth). Empty list => no auth required. if os.environ.get("API_KEYS", "").strip(): cfg["api_keys"] = [k.strip() for k in os.environ["API_KEYS"].split(",") if k.strip()] elif os.environ.get("API_KEY", "").strip(): cfg["api_keys"] = [os.environ["API_KEY"].strip()] else: cfg.setdefault("api_keys", ["sk-gemini"]) # 5. persist resolved config (prefer app dir; fall back to HOME for safety) cfg_path = os.path.join(APP_DIR, "config.json") try: with open(cfg_path, "w") as f: json.dump(cfg, f, indent=2) cookie_file_base = "cookie.txt" except PermissionError: # read-only app dir -> write into the user home instead cfg_path = os.path.join(os.path.expanduser("~"), "config.json") with open(cfg_path, "w") as f: json.dump(cfg, f, indent=2) cookie_file_base = os.path.join(os.path.expanduser("~"), "cookie.txt") # 4. cookie (Pro / authenticated routing). Write to a file and wire it in. cookie = os.environ.get("COOKIE", "").strip() if cookie: cookie_path = cookie_file_base if not os.path.isabs(cookie_path): cookie_path = os.path.join(APP_DIR, cookie_path) with open(cookie_path, "w") as f: f.write(cookie) cfg["cookie_file"] = cookie_path print("[run.py] COOKIE secret detected -> writing", cookie_path) else: cfg["cookie_file"] = None print("[run.py] no COOKIE set -> anonymous mode") # re-dump config so the final cookie_file value is persisted with open(cfg_path, "w") as f: json.dump(cfg, f, indent=2) print(f"[run.py] listening on {cfg['host']}:{cfg['port']}") print(f"[run.py] base url http://0.0.0.0:{cfg['port']}/v1") print(f"[run.py] api keys {'set (' + str(len(cfg['api_keys'])) + ')' if cfg['api_keys'] else 'none (open)'}") # 6. hand off to the server, passing explicit config so overrides win os.chdir(APP_DIR) os.execvp(sys.executable, [sys.executable, "-m", "gemini_web2api", "--config", cfg_path]) if __name__ == "__main__": main()