feat(browser): auto-commit con 178 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 18:22:23 +02:00
parent 7d100e7f3e
commit 763e06c127
178 changed files with 19917 additions and 317 deletions
@@ -0,0 +1,118 @@
"""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}")