---
name: cdp_eval
kind: function
lang: py
domain: browser
version: "1.0.0"
purity: impure
signature: "def cdp_eval(expression: str, *, port: int = 9222, target_url_substr: str = '', await_promise: bool = False, timeout_s: float = 10.0) -> dict"
description: "Evalua una expresion JavaScript en una pestana de un Chrome con remote debugging, eligiendo el target por substring de su URL. Primitiva de transport CDP reutilizable para operar el navegador diario por codigo sin abrir ventana nueva."
tags: [cdp, browser, automation, python, navegator]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["json", "urllib.request", "websocket"]
params_schema:
  params:
    - name: expression
      desc: "Expresion JavaScript a evaluar en el contexto de la pagina (ej. 'document.title', 'document.querySelector(\".x\").click()')."
    - name: port
      desc: "Puerto de remote debugging de Chrome. Default 9222."
    - name: target_url_substr
      desc: "Substring que debe contener la URL del target (pestana). Si vacio, usa el primer target de tipo 'page'."
    - name: await_promise
      desc: "Si True, espera a que la expresion resuelva una Promise antes de devolver el valor (awaitPromise de CDP). Default False."
    - name: timeout_s
      desc: "Timeout en segundos para la conexion WebSocket. Default 10.0."
  output: "dict {ok: bool, value: <valor Python serializable o None>, error: str, target_url: str}. ok=True si la evaluacion produjo valor sin excepcion. Nunca lanza: errores de red/conexion/excepcion JS se devuelven en 'error'."
tested: true
tests: ["test_golden_selecciona_target_por_substr_y_devuelve_value", "test_edge_substr_sin_match_devuelve_ok_false", "test_error_urlopen_lanza_devuelve_ok_false"]
test_file_path: "python/functions/browser/cdp_eval_test.py"
file_path: "python/functions/browser/cdp_eval.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from browser.cdp_eval import cdp_eval

# Requiere un Chrome lanzado con --remote-debugging-port=9222
# y una pestana de WhatsApp Web abierta.
res = cdp_eval("document.title", port=9222, target_url_substr="whatsapp")
print(res["value"])   # -> "WhatsApp" (o None si no hay target)
print(res["ok"], res["target_url"])
```

O directo por CLI: `python3 python/functions/browser/cdp_eval.py "document.title" "whatsapp"`.

## Cuando usarla

Cuando quieras leer datos o ejecutar JS (focus, querySelector, `element.click()`, scroll)
sobre una pestana **ya abierta** del navegador diario sin abrir ventana nueva ni darle
foco al sistema. Es la primitiva de transport sobre la que se construyen las funciones
`whatsapp_*_py_browser` y cualquier script que opere una pestana existente via CDP.

## Gotchas

- Requiere un Chrome lanzado con `--remote-debugging-port=9222` (o el puerto que pases). Sin remote debugging, `GET /json` falla y devuelve `ok=False`.
- `target_url_substr` hace match de **substring** sobre la URL del target (no regex). El primer `page` que contenga el substring gana.
- `returnByValue` solo serializa valores JSON (str, num, bool, list, dict, None). Los nodos del DOM NO son serializables: devuelve la expresion ya reducida a un valor (ej. `el.textContent`, no `el`).
- Para **escribir** en inputs o `contenteditable` NO uses `document.execCommand` ni `el.value = ...`: editores como React/Lexical (WhatsApp Web) ignoran esos cambios programaticos. Usa `cdp_type_chars_py_browser` (teclea caracter a caracter via CDP `Input.dispatchKeyEvent`).
- Nunca lanza: errores de red, conexion WS o excepciones de la propia evaluacion JS se reportan en el campo `error` con `ok=False`.
