---
id: "56"
title: "Funciones globales del registry para control de navegador (find by text, click by text, scroll, HAR, jsonld, opengraph, ...)"
status: pendiente
type: epic
domain:
  - browser
scope: cross-stack
priority: media
depends: []
blocks: []
related: []
created: 2026-05-09
updated: 2026-05-17
tags: []
---

## Contexto

`functions/browser/` (Go) tiene hoy 12 primitivas CDP de bajo nivel: `cdp_connect`, `cdp_navigate`, `cdp_evaluate`, `cdp_get_html`, `cdp_screenshot`, `cdp_click` (por selector CSS), `cdp_type_text`, `cdp_wait_load`, `cdp_wait_element`, `cdp_set_cookie`, `cdp_close`, `chrome_launch`. Todas reutilizables, todas testeadas.

Estas primitivas son suficientes para muchos casos pero **siguen siendo bajo nivel**: el consumidor (cdp-cli, navegator_dashboard, agentes) tiene que componer manualmente patrones que se repiten una y otra vez. Falta una capa intermedia de funciones de mas alto nivel que **resuelvan tareas reales** sin que cada caller las re-componga.

## Objetivo

Ampliar `functions/browser/` (Go preferentemente, Python cuando aplique) con primitivas componibles que resuelvan los patrones recurrentes:

1. **Localizacion robusta de elementos**: por texto visible, por XPath, por aria-label.
2. **Acciones combinadas**: click por texto, fill form, submit, scroll-to, wait-navigation.
3. **Captura estructurada**: HAR, jsonld, opengraph, meta tags, schema.org.
4. **Navegacion segura**: navegacion + wait + retry + screenshot diff.
5. **Multi-tab / multi-instance**: enumerar tabs de N instancias, copiar cookie entre profiles.

Cualquier app del registry que controle browsers (navegator_dashboard, cdp-cli, graph_explorer, futuras) consume esta capa en vez de reimplementar.

## Funciones propuestas (lista inicial)

Todas en `functions/browser/`. Lenguaje preferido entre parentesis. Pureza siempre `impure` (todas tocan red/IO).

### Localizacion / queries

| ID | Que hace |
|---|---|
| `cdp_find_by_text_go_browser` | Devuelve selector CSS unico del primer elemento cuyo `innerText` contiene un texto dado. Filtros: `tag`, `case_sensitive`, `exact`. |
| `cdp_find_all_by_text_go_browser` | Como el anterior pero retorna lista de selectores. |
| `cdp_find_by_xpath_go_browser` | Equivalente con XPath. |
| `cdp_find_by_aria_go_browser` | Por `aria-label` o `role`. Util en SPAs accesibles. |
| `cdp_get_attribute_go_browser` | `<selector>.getAttribute(name)`. |
| `cdp_count_elements_go_browser` | `document.querySelectorAll(sel).length`. |

### Acciones

| ID | Que hace |
|---|---|
| `cdp_click_text_go_browser` | `find_by_text` + `click`. Errores claros si no encuentra. |
| `cdp_fill_form_go_browser` | Recibe `map[selector]value`, hace click + type por cada uno. |
| `cdp_submit_form_go_browser` | Click en boton submit + `wait_navigation`. |
| `cdp_scroll_to_go_browser` | `selector.scrollIntoView({block:'center'})` + verifica que esta en viewport. |
| `cdp_press_key_go_browser` | `Input.dispatchKeyEvent` con tecla nombrada (Enter, Tab, Escape, ArrowDown). |
| `cdp_hover_go_browser` | `Input.dispatchMouseEvent` mouseMoved sobre el elemento. |

### Esperas robustas

| ID | Que hace |
|---|---|
| `cdp_wait_navigation_go_browser` | Espera `Page.frameNavigated` + `Page.loadEventFired` con timeout. |
| `cdp_wait_text_go_browser` | Espera a que aparezca un texto en cualquier sitio del DOM. |
| `cdp_wait_url_go_browser` | Espera a que `location.href` matchee un regex. |
| `cdp_wait_idle_go_browser` | Espera a que la red este idle N ms (Network.* events). |

### Captura estructurada

| ID | Que hace |
|---|---|
| `cdp_har_record_go_browser` | Subscribe `Network.*` durante una accion, retorna HAR JSON estandar. |
| `extract_jsonld_py_browser` | Parse `<script type="application/ld+json">` del HTML. |
| `extract_opengraph_py_browser` | Parse `<meta property="og:*">`. |
| `extract_meta_basic_py_browser` | Title, description, canonical, lang, charset. |
| `extract_schema_org_py_browser` | Microdata `itemtype="https://schema.org/..."`. |
| `extract_forms_py_browser` | Lista forms del HTML con campos, action, method. |
| `cdp_dump_cookies_go_browser` | `Network.getAllCookies` → JSON. |
| `cdp_screenshot_element_go_browser` | Screenshot recortado al bounding box del selector. |

### Multi-tab / multi-instance

| ID | Que hace |
|---|---|
| `cdp_list_tabs_go_browser` | GET `/json` → lista parseada de tabs. |
| `cdp_focus_tab_go_browser` | POST `/json/activate/{id}`. |
| `cdp_close_tab_go_browser` | POST `/json/close/{id}`. |
| `cdp_new_tab_go_browser` | POST `/json/new?url=...`. |
| `cdp_copy_cookies_go_browser` | Lee cookies de un profile, las pone en otro (ambos via CDP). |

### Composiciones (`pipelines`)

| ID | Que hace |
|---|---|
| `browser_login_form_pipeline` | navigate + wait + fill_form + submit + wait_navigation + dump_cookies. Util para pre-logins en perfiles aislados. |
| `browser_screenshot_diff_pipeline` | navigate + screenshot + comparar contra golden + reportar. |
| `crawl_seeded_pipeline` | navigate seeds + extract links + push a queue + dedup + extract elegido. |

## Criterios de calidad para añadir una funcion

Antes de delegar a `fn-constructor`:

- **Patron real recurrente**, no especulativo. Si solo lo usa un caller hoy, esperar.
- **API simple**: max 5 parametros con valores por defecto sensatos.
- **Tests obligatorios** contra Chrome local (env-gated, no CI default — ver `cdp_*_test.go` actuales).
- **Documentacion `.md`** con ejemplo invocacion + caso de uso real (que app/agente la consume).
- **Sin estado global**: cada llamada recibe `*CDPConn`, no asume singleton.

## Estrategia de adopcion

Por **demanda**, no anticipado:

1. Cuando una capa del dashboard (navegator/0001) o un enricher de graph_explorer requiera componer un patron, primero buscar si ya existe en el registry.
2. Si no existe y se usa **dos veces o aparece en TODO**, abrir sub-issue de este 0070 con la firma propuesta.
3. Delegar a `fn-constructor` con los criterios de arriba.
4. Migrar a la nueva funcion el caller que la pidio + cualquier otro que estuviera reimplementando lo mismo.

No precrear todas a la vez — riesgo de funciones zombies que nadie usa.

## Sub-issues iniciales (los que el dashboard v2 va a pedir)

- `0070a` — `cdp_find_by_text_go_browser` + `cdp_click_text_go_browser`. Bloquea Tab Detail y Recipes.
- `0070b` — `cdp_har_record_go_browser`. Bloquea Network panel.
- `0070c` — `extract_jsonld_py_browser` + `extract_opengraph_py_browser`. Bloquea `/extract/structured`.
- `0070d` — `cdp_list_tabs_go_browser` + `cdp_new_tab_go_browser`. Bloquea Tabs panel del dashboard.

Resto se abren cuando se necesiten.

## Relacion con `cdp-cli`

Cuando una funcion nueva se añade al registry, `cdp-cli` (`projects/osint_graph/apps/graph_explorer/cdp-cli/`) gana un subcomando que la envuelve. Asi tanto el dashboard (in-process) como agentes/scripts (subprocess) tienen acceso. Patron ya establecido con las 10 funciones actuales.

## Riesgos

| Riesgo | Mitigacion |
|---|---|
| Funciones zombies si las pre-creamos | Solo crear cuando hay 2 callers o demanda real. |
| Fragmentacion: tener 50 funciones de queries similares | Revision de overlap antes de delegar a fn-constructor. Preferir parametros sobre nuevas funciones. |
| Tests dependen de Chrome local — flakiness en CI | Tag `slow` + env-gate. CI nocturno opcional. |
| Diferencias entre Chrome versions (selectores cambian, eventos rebrandean) | Tests cubren la API CDP estable, no el DOM de sitios externos. |

## Definicion de hecho

Cuando navegator_dashboard v3 cierre, las funciones invocadas por sus endpoints `/extract/*`, `/crawl`, panel Network, panel Tabs y panel Tab Detail viven todas en `functions/browser/` o `python/functions/browser/` (segun lenguaje), no inline en el dashboard. El dashboard es 100% orquestador.
