Files
fn_registry/dev/issues/0098-navegator-extractions-enhancement.md

9.6 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0098 Navegator extractions enhancement (Pick + Network rows + AutoExtract + data_factory bridge) pendiente feature
data-ingest
multi-app alta
2026-05-17 2026-05-17

0098 — Navegator extractions enhancement (Pick + Network rows + AutoExtract + data_factory bridge)

Status: pendiente Created: 2026-05-16 Type: feature Priority: alta Depends: 0097 (data_factory v1 — DONE)

Problema

navegator_dashboard ya tiene paneles Browsers/Tabs/Tab Detail/Network/Agent. Pero extraccion de datos sigue siendo manual: abre DevTools, copia selectores, escribe JS, parsea respuestas. No hay flujo "URL -> esquema -> recipe -> run".

data_factory (issue 0097) tiene nodos vacios. Necesita una via para crearlos rapido desde una pagina web.

Objetivo

Anadir 4 features convergentes:

  1. Element picker (panel Pick / Tab Detail): click sobre elementos -> selector CSS robusto.
  2. Network -> rows: parse JSON responses XHR/fetch -> tabla -> CSV/Save recipe.
  3. AutoExtract IA: URL -> abrir tab -> capturar accessibility tree (no HTML completo) -> claude -p propone schema + selectors -> preview -> save recipe.
  4. data_factory bridge: cada recipe ejecutada -> registra run en data_factory.runs + crea node con kind=extractor.

Decisiones tecnicas

Decision Eleccion
LLM API claude -p "<prompt>" CLI subprocess (NO API key)
Modelo Sonnet (default de claude -p)
Page representation para LLM CDP Accessibility tree (Accessibility.getFullAXTree) + paginacion. NO HTML completo
Recipe format YAML en ~/.dagu/dags/recipes/<slug>.yaml o projects/navegator/profiles/<profile>/recipes/<slug>.yaml
Persistencia filesystem YAML + entry en data_factory.nodes cuando se guarda
Pagination accessibility tree truncado por chunks (~25KB chars) con nextPageToken simulado

Por que accessibility tree

  • Semantico: roles, labels, valores. Sin estilo, sin scripts, sin SVG.
  • Tipico 5-20x mas pequeño que HTML para misma info.
  • Chrome CDP: Accessibility.getFullAXTree { depth: -1 } devuelve array AXNode. Cada nodo: role, name, value, role.value + childIds.
  • Permite identificar campos via role (button/textbox/link/heading/table/cell) + name.
  • LLM razona mejor sobre AX tree porque encaja con su entrenamiento (apps accessibility).

Componentes

Fase A — Element picker

Panel "Pick" boton dentro Tab Detail.

  1. Inyecta JS via Runtime.evaluate que:
    • Hover -> highlight outline rojo via overlay.
    • Click -> captura: CSS selector (algoritmo nth-of-type ascendente truncado), XPath, textContent, tagName, attributes.
    • console.log({ picked: {...} }).
  2. C++ escucha Runtime.consoleAPICalled via WS, parsea payload.
  3. Render card en panel con los datos. Boton "Copy selector", "Save as data_factory node".

Funciones nuevas:

  • cdp_pick_element_js_browser (string JS snippet, registrada como funcion del registry para reutilizar).

Fase B — Network -> rows

En panel Network ya existente:

  • Filtra responses con content_type: application/json.
  • Click "Parse" en una row -> intenta JSON.parse -> si es array -> renderiza tabla.
  • Si es objeto con array dentro -> autodetect path (busca primer array > 0 elementos).
  • Boton "Save as recipe": genera YAML con url_pattern, intercept_response, schema inferido.

Funciones nuevas:

  • infer_json_rows_schema_py_core (puro, py) — recibe JSON, devuelve {root_path, fields: [{name, type, sample}]}.

Fase C — Recipe YAML + runner

Recipe format:

name: bbva_balance
description: Saldo cuenta principal
url_pattern: "bbva.es/.*/movimientos"
trigger: manual
schedule: ""              # opcional cron
steps:
  - wait_selector: "table.movimientos tbody tr"
  - js: |
      return [...document.querySelectorAll('table.movimientos tbody tr')].map(r => ({
        date: r.cells[0].innerText,
        concept: r.cells[1].innerText,
        amount: parseFloat(r.cells[2].innerText.replace(',', '.')),
      }));
output:
  schema:
    - {field: date, type: string}
    - {field: concept, type: string}
    - {field: amount, type: float}
  format: json
  sink: data_factory.runs

Funciones nuevas:

  • cdp_extract_recipe_py_pipelines (impuro pipeline). Args: recipe_path, [tab_id]. Output: dict rows + status.
    • Si no hay tab abierto con url_pattern -> error claro "no tab matching, open URL manually".
    • Ejecuta cada step. wait_selector -> CDP Runtime.evaluate polling. js -> Runtime.evaluate retorna value.
    • Si output.sink=data_factory.runs -> llama data_factory_record_run_py_pipelines.

Fase D — LLM proposer (claude -p)

Panel AutoExtract nuevo:

UI:

  • Input URL.
  • Boton "Open & Analyze" -> abre nueva tab Chrome (CDP Target.createTarget).
  • Wait Page.loadEventFired.
  • Captura accessibility tree via Accessibility.getFullAXTree.
  • Trim tree: descarta nodos role=generic sin name/children utiles.
  • Si tree > 25KB: pagina (split por subarboles top-level).
  • Llama claude -p con prompt + chunk[i].
  • Para cada chunk, recibe {fields: [...], notes}. Merge resultados.
  • Render schema propuesto en tabla editable (puedes editar field name, selector, type).
  • Boton "Test extraction" -> ejecuta JS construido a partir del schema + selectors -> preview filas.
  • Boton "Save as recipe" -> escribe YAML + crea node en data_factory.

Funciones nuevas:

  • claude_cli_prompt_py_infra (impura, py) — wrapper subprocess.run(["claude", "-p", prompt]). Captura stdout. Timeout configurable. Error si no encuentra claude en PATH.
  • cdp_get_ax_tree_py_pipelines (impura) — connect to chrome debugging, call Accessibility.getFullAXTree, return trimmed JSON.
  • trim_ax_tree_py_core (puro) — descarta nodos generic-sin-info, colapsa cadenas single-child, devuelve estructura compacta.
  • chunk_ax_tree_py_core (puro) — splittea tree en chunks de N chars max preservando contexto root.
  • llm_propose_scraping_schema_py_infra (impura) — orquesta: trim + chunk + N calls claude -p + merge. Output schema final.
  • cdp_open_url_and_wait_py_pipelines (impura) — abre URL via CDP, waits load event, devuelve tab_id.

Fase E — data_factory bridge

Cuando recipe corre OK:

  1. cdp_extract_recipe_py_pipelines se asegura que node existe en data_factory.nodes (upsert por name).
  2. Llama data_factory_record_run_py_pipelines(node_id, "cdp_extract_recipe_py_pipelines", args=[recipe_path], trigger="manual").
  3. UI navegator_dashboard muestra link "Open in data_factory" al lado del recipe (futuro tab nav cross-app).

Fase F — Recipes panel

Nuevo panel "Recipes":

  • Tabla: name | url_pattern | schedule | last_run_status | last_run_at | rows_last_run
  • Acciones por row: Run, Edit (abre YAML en selectable_text editable), Delete, Open in data_factory.
  • Filtro por tag/url_pattern.

Fase G — e2e_checks + deploy

app.md e2e_checks:

  • build_windows (ya existe).
  • exe_present (ya existe).
  • api_health (ya existe).
  • claude_cli_availablecommand -v claude exit 0.

redeploy_cpp_app_windows navegator_dashboard projects/navegator/apps/navegator_dashboard --build.

Riesgos

Riesgo Mitigacion
claude -p no instalado en PATH check al arrancar app + tooltip "install claude code CLI". Boton AutoExtract deshabilitado.
AX tree gigante (paginas tipo dashboards admin) trim + chunk + max 5 chunks por URL. Notas claras si truncamos.
LLM propone selectors fragiles user edita antes de save. Recipe versionada YAML.
Recipe corre contra tab equivocada url_pattern + match estricto. Confirma antes de run.
Cookies/sesion no persisten v1 asume user mantiene chrome con sesion abierta. v2: cookies/auth manager.
claude -p lento (5-15s) UI spinner + cancel button. No bloquea otros paneles.
Recipe YAML format inconsistente validator schema-check antes de save. Funcion validate_recipe_yaml_py_core.

No-objetivos v1

  • Cookies/auth manager (Fase F future).
  • Headless scraping (asume chrome visible).
  • Multi-page navigation dentro de recipe (1 url = 1 recipe).
  • Scheduled recipes via cron (se delega a dag_engine: DAG step function: cdp_extract_recipe_py_pipelines args: [recipe_path]).

Funciones nuevas (resumen, ~9)

ID Lang Purity
claude_cli_prompt_py_infra py impure
cdp_pick_element_js_browser js (str) n/a
cdp_get_ax_tree_py_pipelines py impure
trim_ax_tree_py_core py pure
chunk_ax_tree_py_core py pure
llm_propose_scraping_schema_py_infra py impure
cdp_open_url_and_wait_py_pipelines py impure
cdp_extract_recipe_py_pipelines py impure
infer_json_rows_schema_py_core py pure
validate_recipe_yaml_py_core py pure

Tag de capability group: navegator (nuevo, >=3 funciones encajan). Mother page en docs/capabilities/navegator.md.

Aceptacion

  • 4 paneles nuevos visibles: Pick (Tab Detail), AutoExtract, Recipes. Network panel extendido con boton "Parse JSON".
  • claude -p invocable desde la app si CLI disponible.
  • Test E2E: abro URL https://news.ycombinator.com -> autoextract -> obtengo schema {title, url, points, comments} -> save recipe -> run -> >=20 rows -> aparece run en data_factory.
  • redeploy_cpp_app_windows navegator_dashboard pass + exe corriendo.
  • fn doctor cpp-apps OK para navegator_dashboard.
  • 1 recipe canonica salvada en repo como ejemplo.

Telemetria objetivo

  • Capability group navegator con >=8 funciones.
  • data_factory.nodes con >=3 nodos kind=extractor creados via recipe.
  • data_factory.runs con runs reales.