--- name: browser_profile_register kind: function lang: py domain: browser version: "1.0.0" purity: impure signature: "def browser_profile_register(profile_dir: str, label: str = '', persona: str = '', purpose: str = '', note_path: str = '', tags: list | None = None, notes: str = '', user_data_dir: str = '', status: str = 'active', accounts: list | None = None, base_url: str = 'http://127.0.0.1:8771') -> dict" description: "Registra o actualiza un perfil de Chromium (y opcionalmente sus cuentas) en el catalogo del service osint_db (FastAPI + DuckDB single-writer) usado para investigaciones multicuenta OSINT. En una sola llamada hace POST /api/browser-profile con la metadata del perfil (upsert idempotente sobre profile_dir) y un POST /api/browser-profile/account por cada cuenta de la lista accounts. El service responde SIEMPRE HTTP 200 con body {status:ok|error}, se parsea el body. Impura (red). No lanza: devuelve dict de estado. secret_ref de cada cuenta es una REFERENCIA al secreto (ej. 'pass show osint/p1/gmail'), nunca el password en claro." tags: [browser-profiles, osint, chromium, profile, multicuenta] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_py_core" imports: [] tested: true tests: ["test_golden_registra_perfil_con_dos_cuentas", "test_edge_cuenta_invalida_se_reporta_y_no_se_envia", "test_error_post_perfil_falla_devuelve_status_error"] test_file_path: "python/functions/browser/browser_profile_register_test.py" file_path: "python/functions/browser/browser_profile_register.py" params: - name: profile_dir desc: "Nombre del directorio real del perfil Chromium (ej. 'Profile 1', 'Default', 'osint_01'). Es la PK; el upsert es idempotente sobre el." - name: label desc: "Etiqueta humana del perfil (ej. 'Persona Maria - OSINT'). '' para omitir." - name: persona desc: "Identidad/alias ficticio asociado al perfil (sock puppet). '' para omitir." - name: purpose desc: "Proposito de la investigacion (ej. 'rastreo cuentas falsas'). '' para omitir." - name: note_path desc: "Ruta (rel al vault OSINT) de la nota ligada al perfil. '' para omitir." - name: tags desc: "Lista de strings de etiquetas del perfil (ej. ['osint','sock-puppet']). None -> []." - name: notes desc: "Notas libres sobre el perfil. '' para omitir." - name: user_data_dir desc: "user-data-dir de Chromium si NO es el default del wrapper chromium-cdp. '' -> el perfil hereda el default al abrirlo con browser_profile_open." - name: status desc: "Estado del perfil (active|archived|burned...). Default 'active'." - name: accounts desc: "Lista de dicts de cuentas a registrar: {service, identity, secret_ref?, role?, status?, notes?}. None -> sin cuentas. service ej. 'gmail', identity ej. 'x@y.com' o '@handle'. secret_ref es REFERENCIA al secreto, NUNCA el password." - name: base_url desc: "Base del service osint_db. Default http://127.0.0.1:8771." output: "dict de estado. Caso ok: {status:'ok', profile_dir, accounts (int: cuentas registradas con exito), account_errors (list: errores por cuenta invalida o rechazada, vacia si todo OK)}. Caso error (fallo del POST del perfil): {status:'error', error: str}." --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from browser.browser_profile_register import browser_profile_register res = browser_profile_register( "Profile 1", label="Persona Maria - OSINT", persona="maria_ficticia", purpose="rastreo cuentas falsas", tags=["osint", "sock-puppet"], accounts=[ {"service": "gmail", "identity": "maria@example.com", "secret_ref": "pass show osint/p1/gmail"}, {"service": "x", "identity": "@maria_fake", "role": "primary"}, ], ) print(res["status"]) # "ok" si el service esta vivo print(res["accounts"]) # 2 (cuentas registradas) ``` ## Cuando usarla Cuando crees un perfil nuevo de Chromium para una investigacion multicuenta OSINT y quieras dejarlo catalogado (con su persona, proposito y cuentas) en el service osint_db. Llamala tambien para ACTUALIZAR un perfil existente: el upsert es idempotente sobre `profile_dir`, asi que reejecutarla con mas cuentas o metadata nueva no duplica nada. Es el punto de entrada del grupo `browser-profiles`; luego se lista con `browser_profile_list`, se inspecciona con `browser_profile_show` y se abre con `browser_profile_open`. ## Gotchas - **Impura**: hace red (HTTP POST al service). El service `osint_db` debe estar vivo en `http://127.0.0.1:8771`. Si esta caido, devuelve `{status:'error', error:'... inaccesible'}` sin lanzar. - **El codigo HTTP NO indica exito**: el service responde SIEMPRE HTTP 200 con body `{status:ok|error}`. La funcion parsea el body, no el codigo HTTP. - **secret_ref NUNCA es el password**: es una REFERENCIA al secreto (ej. `"pass show osint/p1/gmail"`). No metas credenciales en claro — se resuelven con `pass` en el momento de usarlas. - **Idempotente**: reejecutar con el mismo `profile_dir` actualiza (upsert), no duplica. Lo mismo para cada cuenta (PK `::`). - **Errores parciales de cuentas**: si el perfil se registra pero una cuenta falla (o le falta `service`/`identity`), el `status` global sigue siendo `"ok"` y el detalle del fallo va en `account_errors`. Solo `status:'error'` si falla el POST del PERFIL. - **Single-writer DuckDB**: la DB la abre el service. NUNCA abrir `osint.duckdb` en paralelo; todo pasa por HTTP. ## Notas Usa el helper compartido `python/functions/browser/_osint_db_client.py` (modulo privado no indexado) para el POST sobre `urllib.request` de stdlib (sin `requests`). Timeout HTTP de 10s por request.