"""Lanza Chromium en un perfil del catalogo osint_db y expone sus cuentas/secret_refs. Wrapper que compone `browser_profile_show` (para leer la metadata del perfil y sus cuentas desde el service osint_db) y luego lanza Chromium en ese perfil. Devuelve las cuentas con sus `secret_ref` (REFERENCIAS a secretos, nunca el password) para que el operador sepa que credenciales usar. GOTCHAS de este entorno (Linux nativo de enmanuel): - El wrapper `/usr/bin/chromium` ya inyecta `--user-data-dir=$HOME/.config/chromium-cdp` y `--remote-debugging-port=9222` via `/etc/chromium.d/cdp`. Por eso, si el `user_data_dir` resuelto ES ese default, NO se pasa `--user-data-dir` (se hereda del wrapper); si es OTRO directorio, se pasa explicito. - Lanzar chromium directamente como hijo da exit-144 en este entorno. Se lanza SIEMPRE via `systemd-run --user --scope --` (proceso aislado), en background, sin esperar. Funcion impura: hace red (HTTP al service) y lanza un proceso. No lanza excepciones; devuelve un dict de estado. Con `dry_run=True` no abre nada (devuelve el comando). """ import os import subprocess from browser.browser_profile_show import browser_profile_show # Default del wrapper /etc/chromium.d/cdp en esta maquina (se compara expandido). _DEFAULT_USER_DATA_DIR = os.path.expanduser("~/.config/chromium-cdp") def browser_profile_open( profile_dir: str, url: str | None = None, base_url: str = "http://127.0.0.1:8771", dry_run: bool = False, ) -> dict: """Lanza Chromium en el perfil indicado y devuelve sus cuentas/secret_refs. Args: profile_dir: nombre del directorio real del perfil Chromium (ej. "Profile 1", "osint_01"). Debe existir en el catalogo osint_db. url: URL a abrir al arrancar (ej. "https://mail.google.com"). None -> sin URL. base_url: base del service osint_db. Default http://127.0.0.1:8771. dry_run: si True, NO lanza nada; devuelve el comando que lanzaria. Util para test y para revisar el comando antes de abrir el navegador. Returns: Caso dry_run ok: {"status":"ok", "profile_dir": str, "cmd": list[str] (argv que se lanzaria), "accounts": list de dicts {service, identity, secret_ref, role}}. Caso real ok: {"status":"ok", "profile_dir": str, "launched": True, "cmd": list[str], "accounts": list de dicts {service, identity, secret_ref, role}}. Caso perfil no existe / service caido: {"status":"error", "error": str} (no lanza). """ try: meta = browser_profile_show(profile_dir, base_url=base_url) if meta.get("status") != "ok": # Perfil inexistente o service caido: propaga el error sin lanzar nada. return meta profile = meta.get("profile", {}) raw_accounts = meta.get("accounts", []) accounts = [ { "service": a.get("service"), "identity": a.get("identity"), "secret_ref": a.get("secret_ref"), "role": a.get("role"), } for a in raw_accounts ] # Resolver user_data_dir: el de la fila si no esta vacio; si no, el default del wrapper. row_udd = (profile.get("user_data_dir") or "").strip() resolved_udd = os.path.expanduser(row_udd) if row_udd else _DEFAULT_USER_DATA_DIR chromium_args = ["chromium", f'--profile-directory={profile_dir}'] # Solo pasar --user-data-dir si NO es el default del wrapper (que ya lo inyecta). if os.path.normpath(resolved_udd) != os.path.normpath(_DEFAULT_USER_DATA_DIR): chromium_args.append(f"--user-data-dir={resolved_udd}") if url: chromium_args.append(url) # Lanzamiento aislado para evitar exit-144 (ver gotcha del modulo). cmd = ["systemd-run", "--user", "--scope", "--", *chromium_args] if dry_run: return { "status": "ok", "profile_dir": profile_dir, "cmd": cmd, "accounts": accounts, } # Background, sin esperar: no bloquear al operador ni capturar el navegador. subprocess.Popen( cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) return { "status": "ok", "profile_dir": profile_dir, "launched": True, "cmd": cmd, "accounts": accounts, } except Exception as e: # noqa: BLE001 - contrato: nunca lanzar return {"status": "error", "error": f"{type(e).__name__}: {e}"} if __name__ == "__main__": # Smoke contra un puerto muerto: el service caido -> browser_profile_show falla, # browser_profile_open propaga el error sin abrir navegador. res = browser_profile_open("Profile 1", url="https://example.com", base_url="http://127.0.0.1:1", dry_run=True) assert res["status"] == "error", res print("browser_profile_open smoke OK (service caido -> status error, sin lanzar)") print(f" {res}")