Skip to main content
Glama

Daedalus

Control remoto del navegador mediante una extensión de Chrome. Puente de eval + hotfixes persistentes + control por pestaña + capturas de pantalla, CDP, cookies, captura de red en páginas donde Chrome ejecuta la extensión.

Instalación

Debes saber esto antes de instalar: el eval ordinario usa inyección en el mundo MAIN sin banner. Cuando una sonda CSP sin fuente no puede establecer que la compilación dinámica está disponible, Daedalus intenta el fallback de CDP; una conexión exitosa muestra el banner "Daedalus started debugging this browser" de Chrome mientras ese fallback se ejecuta. Una sesión CDP mantenida o la captura de red pueden mantener la conexión por más tiempo. El banner identifica una conexión de depurador, no una garantía de integridad de valor.

Carga la extensión sin empaquetar (extension/) en Chrome:

  1. Visita chrome://extensions

  2. Activa Modo de desarrollador

  3. Haz clic en Cargar descomprimida, selecciona extension/

Se genera automáticamente un token único en la primera instalación y se almacena en chrome.storage.local. Puedes verlo/cambiarlo desde la página de opciones de la extensión (icono de puzle → Daedalus → Opciones).

Para el raspado paralelo de múltiples pestañas, desactiva la limitación de pestañas en segundo plano de Chrome:

chrome --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgrounding

El puente en sí (server.py) solo usa la biblioteca estándar y no necesita instalación. La CLI y el endpoint MCP en proceso son el paquete de Python de este repositorio:

pip install .            # the `daedalus` CLI
pip install ".[mcp]"     # ... and the MCP endpoint's dependencies

Sin el extra mcp, el puente igualmente se inicia y sirve todo lo demás; informa del fallo de arranque de MCP al inicio y /mcp no se sirve.

Related MCP server: Real Browser MCP

Cómo funciona

  1. Token: Se genera una vez en la instalación mediante crypto.randomUUID(), se almacena en chrome.storage.local

  2. IDs de pestaña: La API nativa tabs de Chrome — cada pestaña se identifica por su tabId de Chrome, se registra en creación/actualización con un latido de chrome.alarms de 30s

  3. Flujo SSE único: background.js abre una conexión SSE fetch persistente (tab=extension) y despacha los comandos entrantes a la pestaña correcta

  4. Puente de página: content.js (mundo ISOLATED) retransmite los mensajes window.GM entre el fondo y page.js (mundo MAIN). El eval usa primero la inyección en el mundo MAIN de chrome.scripting. Una sonda de compilación dinámica sin fuente enruta las páginas bloqueadas por CSP a CDP; si falla la conexión allí, se llega al relé de página. Cada canal se ejecuta con semántica del mundo MAIN de la página.

  5. Hotfixes: Se almacenan en la clave daedalus-hotfixes de chrome.storage.local (no por token), se reproducen en cada página de nivel superior elegible al cargar — inyección en el mundo MAIN, o CDP cuando la CSP de la página prohíbe la compilación dinámica — y están sujetos a versión para los arreglos no permanentes. Rotar el token no aísla ni borra este almacén.

Envío de comandos

Daedalus expone su superficie de comandos de extensión como un servidor MCP en <your-bridge>/mcp (transporte streamable-HTTP). Antes de despachar una solicitud, exige que el valor Bearer coincida exactamente con el token del puente resuelto a través de la ruta de configuración existente de la CLI: TOKEN anula DAEDALUS_TOKEN, incluido un proveedor _settings opcional. Sin token configurado, la superficie MCP falla cerrada con 401. El endpoint solo existe cuando el extra mcp está instalado (ver "Instalación" arriba). Añádelo a Claude Code (o a cualquier cliente MCP) con:

{
  "mcpServers": {
    "daedalus": {
      "url": "https://daedalus.example.com/mcp",
      "headers": { "Authorization": "Bearer <your-bridge-token>" }
    }
  }
}

El token del puente es el que genera la extensión en la instalación — visible en la página de opciones de la extensión (icono de puzle → Daedalus → Opciones).

Ese ejemplo pone el servidor MCP detrás de un nombre de host público, pero los hosts permitidos por defecto del transporte son solo de bucle local (127.0.0.1:*,localhost:*): nombra el host público en DAEDALUS_MCP_ALLOWED_HOSTS o las solicitudes proxy serán rechazadas. Ver "Servidor" abajo para los tres ajustes de MCP.

40 herramientas en 7 grupos — pestañas, eval/depuración, medios, cookies, CSS/bloqueo, hotfixes, red/CDP. Ver la sección <mcp> de CLAUDE.md para la lista completa, o llama a tools/list en el endpoint MCP.

Helper de verificación manual (no necesita cliente MCP):

TOKEN=<tok> python3 scripts/mcp_probe.py list
TOKEN=<tok> python3 scripts/mcp_probe.py call title '{"tab_id":"<tabId>"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call screenshot '{"include_image":true}'

CLI

Una CLI de shell se distribuye como el wheel daedalus-cli en este repositorio (daedalus_cli/), instalada como el comando daedalus. Lee DAEDALUS_URL y DAEDALUS_TOKEN del entorno, con TOKEN como anulación puntual e ID=<tabId> para apuntar a una pestaña específica (omítelo para transmitir):

DAEDALUS_TOKEN=<tok> daedalus tabs
DAEDALUS_TOKEN=<tok> ID=<tabId> daedalus title
DAEDALUS_TOKEN=<tok> daedalus exec myid 'document.title'

daedalus --help lista todos los subcomandos, y cada uno tiene su propio --help. El código exec que envías es una expresión o un cuerpo de función cuyo valor de retorno vuelve como resultado — ver "Envío de comandos" arriba para el contrato.

También hay un punto de integración de importación opcional: si un módulo llamado _settings es importable en sys.path, la CLI usa sus funciones setting(name, default) y required(name) para DAEDALUS_URL y DAEDALUS_TOKEN en lugar del respaldo de entorno integrado. La variable de entorno TOKEN sigue siendo la anulación puntual de token y tiene prioridad; sin _settings, DAEDALUS_URL usa su valor por defecto y DAEDALUS_TOKEN es obligatorio. ID sigue siendo la variable de entorno para apuntar a una pestaña específica.

O publica atómicamente un archivo de comando crudo (sin MCP, sin CLI). La redirección directa al nombre final .json no está soportada porque el flujo puede observar un archivo antes de que el escritor termine. Escribe un nombre hermano que termine en .tmp, luego renómbralo dentro del mismo directorio:

# Broadcast to all tabs
commands_dir="$DAEDALUS_DIR/commands"
final="$commands_dir/<token>.json"
tmp="$(mktemp "$commands_dir/.<token>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
  mv "$tmp" "$final"

# Target a specific tab
final="$commands_dir/<token>_<tabId>.json"
tmp="$(mktemp "$commands_dir/.<token>_<tabId>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
  mv "$tmp" "$final"

El lector ignora los nombres hermanos .tmp. Si un escritor anterior expone JSON malformado en el nombre final, el lector lo deja intacto y reintenta en lugar de borrar una escritura posiblemente en curso. Después del renombrado atómico, el flujo SSE entrega y consume el comando. El resultado cae en $DAEDALUS_DIR/results/<token>_<tabId>.json (por pestaña) y $DAEDALUS_DIR/results/<token>.json (el último escritor gana). Las finalizaciones de eval de inyección page-main y de relé de página pueden llevar un campo exec_ms — el tiempo de ejecución en el contexto de la página en milisegundos. Una página puede falsear u omitir este campo temporizado por página en cualquiera de los dos canales. Las finalizaciones de CDP no llevan exec_ms. Los resultados asociados con una entrega en cola también llevan roundtrip_ms — el tiempo de ida y vuelta completo observado por el servidor en milisegundos, desde la puesta en cola del comando (PUT /command) hasta la llegada del resultado (POST /result); abarca la espera en cola + entrega SSE + relé del cliente + ejecución + viaje de retorno, por lo que roundtrip_ms − exec_ms aproxima la sobrecarga de transporte/cola cuando ambos campos están presentes. Estas mediciones usan relojes diferentes: exec_ms usa performance.now() de la página, mientras que roundtrip_ms usa milisegundos de reloj de pared del servidor, por lo que su diferencia es una aproximación, no una resta exacta. Un marco heredado sin _did no tiene roundtrip_ms.

Soporte asíncrono

El canal por defecto page-main ejecuta expresiones a través del eval propiedad de la página y cuerpos de función a través del Function propiedad de la página; un envoltorio asíncrono proporciona await de nivel superior. Antes de enviar el código fuente, el fondo inyecta solo una sonda constante Function. Si esa sonda devuelve true, la inyección del código fuente se intenta una vez y cada resultado es terminal: un valor, una excepción o un error de transporte de inyección se informa sin reintentar el código fuente en CDP. La página posee Function y puede influir en esta pista de enrutamiento, pero la sonda no contiene código fuente enviado, por lo que elegir otro canal no puede duplicar sus efectos secundarios.

Si la sonda sin fuente no devuelve true, lo más común es que la CSP de la página bloquee la compilación dinámica, Daedalus intenta CDP. Una conexión exitosa muestra el banner de depurador de Chrome. Runtime.evaluate usa el modo REPL para await de nivel superior; el código fuente que contiene return se trata como un cuerpo de función a menos que una sonda envolvente lo analice como una expresión. Esa sonda envolvente es una heurística de análisis, no un límite de seguridad sin ejecución: el código fuente manipulado por el operador puede escaparla y ejecutarse. Después de que se despacha la evaluación final de CDP, cada resultado es terminal. Solo el fallo de conexión o de forma antes de que el código fuente enviado se ejecute llega al relé de página. El acuerdo de promesa de CDP está limitado a 10 segundos, y los manejadores de objetos de resultado y excepción se liberan incluso cuando una sesión mantenida o una captura retiene la conexión del depurador.

El JavaScript evaluado en una página que no controlas devuelve un valor que esa página puede elegir, independientemente del canal que lo ejecutó. El campo world registra solo qué canal de ejecución ejecutó el código fuente enviado; es metadato de diagnóstico para el comportamiento de CSP y depurador, no una señal de confianza. Sus valores de eval son page-main para la inyección ordinaria, cdp para el fallback del inspector y page:<hostname> para el relé final, donde <hostname> es el location.hostname del script de contenido. El fondo añade el prefijo page:, por lo que un hostname de relé no puede colisionar con cdp o page-main; este hecho de espacio de nombres no dice nada sobre el valor devuelto. La CLI muestra el campo como channel=..., el panel hace lo mismo, y MCP exec, put, result y ping preservan el valor exacto de world. Ninguno asigna una clase de confianza.

La inyección por defecto y el fallback de CDP preservan el comportamiento de script clásico para los casos de modo sloppy documentados: with compila, los literales octales heredados son aceptados, y una asignación no declarada crea un global. El modo REPL de CDP también permite declaraciones repetidas let o const. Las esperas de resultado de CLI y MCP tienen un valor por defecto de 15 segundos; un tiempo de espera del llamador no cancela el código que ya se está ejecutando en la página. El relé de Blob espera por separado hasta 10 segundos para el código que contiene await, y 3 segundos en caso contrario, antes de informar un tiempo de espera de fallback.

La correlación del relé sigue siendo descriptiva y limitada: la pestaña del remitente debe coincidir, un mensaje aceptado consume el id de relé aleatorio, y ningún id de relé se registra hasta que la sonda de inyección sin fuente y el fallback de CDP previo al envío hayan terminado. Estos controles previenen la finalización de relé entre pestañas o duplicada; no establecen integridad de valor.

Como máximo 1,000 entradas de relé de página pueden estar activas. Un nuevo fallback en capacidad recibe un error de capacidad terminal sin expulsar el trabajo activo. Cada entrada registrada expira después de 300,000 ms y recibe un error de tiempo de espera terminal si ninguna finalización de la misma pestaña o un fallo de envío anterior la elimina.

Panel

Una superficie de control basada en navegador en <your-bridge>/dashboard que maneja todo el conjunto de comandos de la extensión sin la CLI — lista de pestañas en vivo, REPL de eval, capturas de pantalla, cookies, hotfixes, reglas de bloqueo, captura de red, CDP, inyección de CSS, tiempos de fetch, navegador de subida.

  1. Abre la URL en una pestaña que la extensión controle.

  2. Desplázate a §12 Configuración y pega el token de la página de opciones de la extensión (icono de puzle → Daedalus → Opciones). Guarda.

  3. El punto de estado SSE en la barra superior se vuelve cian cuando el panel está suscrito a eventos en vivo.

Servido directamente por server.py desde el directorio dashboard/ del repositorio (JS vanilla + módulos ES, sin compilación). Las actualizaciones en vivo vienen a través del endpoint /stream existente: server.py pone eventos en cola en commands/<token>_dashboard/<ts>_<uuid>.json cuando /register actualiza una pestaña existente, y en las rutas exitosas de /sync-tabs, /unregister y /result; /unregister emite incluso cuando no había pestaña presente. El panel los drena como marcos kind:'event'.

Advertencia: los scripts de contenido y de página de la extensión se inyectan en páginas coincidentes, incluida la pestaña del panel. Los comandos de eval de difusión (exec -b) también se ejecutan dentro del panel — prefiere apuntar a una pestaña específica o cierra el panel antes de difundir código disruptivo.

Auto-parche

Hotfixes

Persiste pequeños parches que se reproducen en cada carga de página de nivel superior elegible. Mediante herramientas MCP: store_hotfix, list_hotfixes, clear_hotfix, clear_hotfixes, set_permanent. Ejemplo (a través del script de sonda):

TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"my-fix","code":"console.log(\"patched\")"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"always-on","code":"console.log(\"baseline\")","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call set_permanent '{"fix_id":"my-fix","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call list_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfix '{"fix_id":"my-fix"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes '{"include_permanent":true}'

Los hotfixes están sujetos a versión por defecto. Después de un cambio de versión de la extensión, los arreglos no permanentes retenidos se omiten en lugar de eliminarse; almacenar un arreglo actualiza el registro a la versión actual y hace que sus arreglos no permanentes retenidos vuelvan a ser elegibles. Marca un arreglo como permanente (mediante permanent: true en store_hotfix, o set_permanent) para reproducirlo a través de cambios de versión. clear_hotfixes elimina los arreglos no permanentes y mantiene los permanentes por defecto; pasa include_permanent: true para eliminar todo el almacén.

Comandos de extensión

El service worker en segundo plano acepta comandos tipados (emitidos mediante herramientas MCP o escribiendo JSON con "type": "..." en $DAEDALUS_DIR/commands/):

Comando

Propósito

screenshot

Capturar la pestaña visible como PNG

cdp

Emitir llamadas sin procesar del Chrome DevTools Protocol

net-capture / net-capture-stop / net-capture-get

Intercepción completa de solicitudes/respuestas mediante CDP

cookies / set-cookie / remove-cookie / clear-cookies

Acceso al almacén de cookies

open-tab / open-tabs / focus-tab / close-tab / navigate / reload

Control de pestañas

inject-css / remove-css

Inyección de CSS por pestaña

block-requests / unblock-requests / list-block-rules

Bloqueo mediante declarativeNetRequest

store-hotfix / clear-hotfix / clear-all-hotfixes / list-hotfixes / set-permanent

Gestión de hotfixes (las correcciones permanentes sobreviven a los cambios de versión)

ext-reload

Recargar la propia extensión desde el disco

fetch-timings

Búfer circular de diagnóstico para los relés de fetch

Puente GM

window.GM (en page.js, mundo MAIN) proporciona este subconjunto al estilo Tampermonkey:

Método

Descripción

GM.getValue(key, default)

Leer una clave de cadena no reservada de chrome.storage.local de toda la extensión (no por token)

GM.setValue(key, value)

Escribir una clave de cadena no reservada en el almacenamiento de toda la extensión

GM.deleteValue(key)

Eliminar una clave de cadena no reservada del almacenamiento de toda la extensión

GM.listValues()

Listar las claves de almacenamiento no reservadas

GM.xmlhttpRequest(opts)

Solicitud HTTP retransmitida en segundo plano (inmune a CSP)

GM.addStyle(css)

Inyectar CSS

GM.setClipboard(text, type)

Escribir en el portapapeles

GM.notification(opts)

Notificación de escritorio

GM.openInTab(url, opts)

Abrir una nueva pestaña

GM.download(opts)

Activar una descarga

GM.info

Metadatos del script

El acceso a las cookies es una capacidad del operador, no de la página: pasa por los comandos cookies / set-cookie / remove-cookie / clear-cookies autenticados con token mencionados arriba y deliberadamente no se expone al contexto de la página — page.js se ejecuta en cada página de nivel superior que coincida, que de otro modo podría leer cookies que su propio document.cookie no puede ver.

Arquitectura

Browser (matching tab)                           Server (your bridge host)
┌────────────────────────────────────────────┐   ┌──────────────────────┐
│ MAIN world                                 │   │ bridge (server.py)   │
│  ├─ page-main: default injection channel   │   │ /stream?token        │
│  └─ page.js: GM + relay channel            │   │ watches commands/    │
│            ▲                               │   │                      │
│            │ window.postMessage            │   │ /result writes       │
│ content.js (ISOLATED)                      │   │    results/          │
│            ▲                               │   │                      │
│            │ chrome.runtime                │   │                      │
│ background.js (service worker)             │   │                      │
│  ├─ CDP: CSP fallback channel              │   │                      │
│  ├─ single SSE stream ◄────────────────────┼───┤                      │
│  └─ POST result, fetch ────────────────────┼──►│                      │
└────────────────────────────────────────────┘   └──────────────────────┘
  • Un único flujo SSE: El fondo abre una única conexión SSE fetch (tab=extension) y enruta los comandos a la pestaña objetivo mediante chrome.tabs.sendMessage. Un watchdog de 30s fuerza la reconexión en flujos obsoletos.

  • Enrutamiento por pestaña: Los comandos se dirigen a un tabId específico de Chrome o se transmiten a todas las pestañas.

  • Comportamiento CSP: GM.xmlhttpRequest envía el trabajo HTTP al fetch del service worker en segundo plano. La evaluación normalmente usa inyección en el mundo MAIN sin banner. Cuando la sonda sin fuente informa que la compilación dinámica no está disponible, CDP proporciona el respaldo CSP y muestra el banner de depuración de Chrome si se adjunta; un fallo de adjunción llega al relé de página/Blob. El valor world resultante describe ese canal y no hace ninguna afirmación de integridad.

Servidor

Inicie el puente con sus tres ajustes obligatorios — sale al inicio sin DAEDALUS_DIR o DAEDALUS_PORT, y cada ruta de control del puente falla de forma segura sin un token configurado:

DAEDALUS_DIR=<data-dir> DAEDALUS_PORT=<port> DAEDALUS_TOKEN=<bridge-token> python3 server.py

server.py se ejecuta bajo el supervisor que use (aquí una unidad systemd). Ponga un proxy inverso con terminación TLS delante si lo expone más allá de localhost; el puente mismo habla HTTP plano. Las rutas de control del puente y de almacenamiento comparan su token con un secreto configurado, resuelto a través de la ruta de configuración CLI: TOKEN es la anulación de una sola vez; de lo contrario, se requiere DAEDALUS_TOKEN (y un módulo _settings incrustado puede proporcionarlo). La configuración faltante y las discrepancias fallan de forma segura. Solo las rutas orientadas a la página POST /segment y GET /segment-status usan en su lugar una capacidad con ámbito de trabajo.

El front-end MCP en proceso toma tres ajustes opcionales, documentados juntos porque describen un único listener: DAEDALUS_MCP_PORT (por defecto 8086) es el puerto de bucle local al que se enlaza; sus manejadores de herramientas usan la URL de bucle local realmente enlazada del puente, incluso cuando DAEDALUS_PORT=0; DAEDALUS_LOCAL_URL anula explícitamente esa URL para un despliegue MCP independiente que se enfrenta a un puente que se ejecuta en otro lugar; y DAEDALUS_MCP_ALLOWED_HOSTS (por defecto 127.0.0.1:*,localhost:*) es la lista de hosts permitidos separada por comas que su protección contra el rebinding de DNS acepta, por lo que poner /mcp detrás de un nombre de host público significa nombrar ese host allí.

Endpoints: GET /stream, GET /tabs, GET /health, GET /dashboard[/<asset>], POST /register, POST /sync-tabs, POST /unregister, POST /poll, POST /result, PUT /command, GET /result, POST/GET/DELETE /upload, GET /screenshot, POST /segment-job, POST /segment + GET /segment-status. POST /segment-job requiere el token del puente configurado; solo POST /segment y GET /segment-status toman la sig con ámbito de trabajo. Consulte CLAUDE.md para notas sobre cargas útiles y endpoints.

POST /poll consume y elimina el archivo de comando de difusión heredado cuando está presente.

PUT /command pone en cola en una cola de directorio FIFO por destino (los comandos consecutivos a una pestaña ya no se sobrescriben), sella cada uno con un id de entrega (_did) para que la extensión pueda deduplicar un marco reenviado, y elimina por TTL los comandos no reclamados después de DAEDALUS_CMD_TTL segundos (por defecto 90). Un recolector en segundo plano aplica ese TTL y elimina los directorios de cola vacíos incluso cuando ningún consumidor SSE se conecta nunca. GET /health informa la actividad de flujo/registro/última entrega para detectar un puente muerto silenciosamente.

El puente rechaza los portadores de autoridad repetidos en lugar de seleccionar un valor: esto incluye token en cadenas de consulta o cuerpos JSON y job / sig en las rutas de capacidad de segmento, incluso cuando los valores repetidos son iguales o están en blanco. El transporte MCP también rechaza los encabezados Authorization, Mcp-Session-Id, Host y Origin repetidos, además de los argumentos job repetidos para las herramientas de segmento.

Cuando la extensión publica un resultado, el servidor expone el _did del comando como deliveryId y asigna un nuevo resultGeneration. Los esperadores CLI y MCP primero echan un vistazo a la ranura de resultado compartida, comparan tanto el id de comando como el deliveryId, luego consumen condicionalmente esa generación con GET /result?...&consume=1&expected=<resultGeneration>. Si otro resultado reemplaza la ranura entre esas solicitudes, el consumo condicional deja el nuevo resultado en su lugar e informa que no fue consumido. Un consume=1 simple sin expected sigue siendo una lectura de compatibilidad destructiva de la ranura actual.

GET /stream mantiene la conexión SSE abierta indefinidamente, demostrando actividad con un comentario de keepalive cada DAEDALUS_STREAM_KEEPALIVE segundos (por defecto 15) en lugar de ciclar la conexión con un temporizador; DAEDALUS_STREAM_MAX_AGE (por defecto 3600) es solo un techo de último recurso. La ruta de cierre está fijada por tests/test_stream_lifecycle.py.

DAEDALUS_MAX_BODY_SIZE (por defecto 64 * 1024 * 1024 bytes, 64 MiB) limita los cuerpos de solicitud leídos por los manejadores POST, PUT y DELETE; un cuerpo declarado mayor que el límite recibe 413, y una solicitud que no declara Content-Length recibe 411 en lugar de leerse como un cuerpo vacío. Auméntelo al retransmitir segmentos más grandes u otras cargas útiles.

Los valores de llamador respaldados por el sistema de archivos usan una política de un solo componente de ruta: rechaza .., caracteres de control C0/C1 y sustitutos, caracteres de ruta no válidos en Windows y nombres de dispositivos, puntos o espacios finales, y codificaciones UTF-8 de más de 240 bytes. Los tokens del puente son más estrictos y también rechazan puntos y guiones bajos. Otros nombres de trabajo UTF-8 se aceptan, por lo que los clientes deben codificarlos en URL en las cadenas de consulta.

POST /segment-job copia tres cuotas fijas en cada nuevo registro de trabajo: DAEDALUS_MAX_SEGMENT_INDEX (por defecto 99999), DAEDALUS_MAX_SEGMENTS_PER_JOB (por defecto 10000) y DAEDALUS_MAX_SEGMENT_JOB_SIZE (por defecto 4 * 1024 * 1024 * 1024 bytes, 4 GiB). Cambiar esos ajustes afecta a los trabajos creados posteriormente; un trabajo con cuotas almacenadas continúa usando sus valores registrados. DAEDALUS_MAX_BODY_SIZE también limita cada solicitud de segmento individual.

Seguridad

Lea esto antes de instalar la extensión.

Inyecta un shim window.GM en cada página de nivel superior que coincida (<all_urls>, mundo MAIN), que es lo que permite que un script enviado con put haga solicitudes de origen cruzado. La consecuencia es la parte de la que debe ser deliberado: cualquier sitio que coincida que visite puede llamar a ese shim, por lo que ese sitio puede emitir solicitudes de origen cruzado a través de los privilegios de la extensión, exactamente como lo haría un administrador de usuarios con un @match comodín. Si eso no es aceptable para su navegación, reduzca los matches en extension/manifest.json a los hosts que realmente maneja, y acepte que el puente entonces no hace nada en otras páginas.

El token del puente y la URL del servidor no son accesibles a través de los métodos de almacenamiento GM de la página. Viven en chrome.storage.local bajo el prefijo daedalus-, y el relé se niega a leer, escribir o enumerar ese espacio de nombres para los scripts de la página. Sin esa regla, cualquier sitio visitado podría leer su token del puente con GM.getValue('daedalus-token') o redirigir el puente con GM.setValue('daedalus-server', ...), silenciosamente. tests/test_repo_contract.py fija la regla.

El origen del hotfix es deliberadamente un estado observable por la página, no un estado confidencial de la extensión. El fondo selecciona las correcciones permanentes más las correcciones no permanentes que coinciden con la versión de la extensión y ejecuta cada una en la página a través de los mismos canales que usa la evaluación ordinaria — inyección en el mundo MAIN donde la página permite la compilación dinámica, CDP donde no — por lo que la página puede observar el origen que ejecuta en cualquiera de los canales.

Un portador MCP no puede nombrar un archivo del host del servidor para que el proceso MCP lo lea. Las herramientas MCP put, inyección/eliminación de CSS y almacenamiento de hotfixes aceptan solo origen en línea. El CLI local mantiene su conveniencia de ruta de archivo, pero el proceso CLI lee ese archivo seleccionado por el operador y envía su contenido en línea. Por lo tanto, tener el token del puente otorga la autoridad documentada de control del navegador y de la extensión, incluida la lectura del origen de hotfix almacenado, pero no agrega una lectura arbitraria del sistema de archivos del host a través de estas herramientas.

Las rutas de control del puente y de almacenamiento requieren el token del puente configurado; /segment y /segment-status son la única excepción de capacidad. El servidor compara los tokens de las solicitudes con el secreto resuelto desde TOKEN o DAEDALUS_TOKEN y rechaza las solicitudes cuando no hay ningún secreto configurado. POST /segment-job requiere ese token del puente porque acuña la capacidad limitada al trabajo; el JavaScript de la página no confiable usa la capacidad solo para publicar y consultar ese trabajo. Cualquiera que tenga el token del puente puede controlar tu navegador. No expongas el puerto del puente más allá de loopback sin un proxy inverso que termine TLS, y trata el token como una credencial.

Los resultados de evaluación no ofrecen garantía de integridad del valor. El JavaScript evaluado en una página que no controlas devuelve un valor que esa página puede elegir, independientemente de qué canal lo haya ejecutado. El campo world indica cómo se ejecutó el código enviado, no si hay que confiar en su valor: page-main es inyección ordinaria en el mundo MAIN, cdp es el respaldo CSP del inspector, y page:<hostname> es el relé. El prefijo obligatorio page: se añade fuera del contexto de la página, por lo que ningún hostname puede producir cdp o page-main; esa resistencia a colisiones es solo descriptiva. En page-main, los enlaces eval y Function propiedad de la página pueden leer el código enviado, así como influir en su valor devuelto.

La compilación CDP evita resolver los enlaces eval y Function de la página, y la implementación recupera los manejadores directos por referencia antes de serializarlos. Esos mecanismos de transporte no cambian el límite de confianza: el código enviado sigue leyendo el estado controlado por la página y puede enrutar cualquier valor, incluido un primitivo, a través de la maquinaria de promesas de la página antes de que CDP lo reciba. Siempre que el código enviado use esas rutas controladas por la página, la página puede elegir el valor devuelto en cdp también.

El relé acepta solo la invocación almacenada asociada con ese id aleatorio, requiere que la pestaña del remitente coincida, y consume la entrada una vez. La degradación no revela el token del puente, la URL del servidor, el id de entrega ni la ruta de resultados, no otorga autoridad adicional de extensión o navegador, y no puede afectar una invocación en otra pestaña. Esas propiedades protegen el enrutamiento y la autoridad del navegador; no convierten el marcador del relé ni su valor JavaScript devuelto en una señal de confianza.

Despliegue

El puente habla HTTP plano en loopback. Las rutas de control del puente y de almacenamiento requieren el token configurado y fallan de forma segura cuando está ausente; solo /segment y /segment-status usan la capacidad limitada al trabajo. Dos cosas que deliberadamente NO hace, porque pertenecen a lo que esté delante de él:

  • TLS y CORS. server.py no envía cabeceras CORS. Cuando el puente es de origen cruzado, las llamadas fetch del lado de la página del relé HLS de ejemplo necesitan acceso a tanto GET /segment-status como POST /segment; el proxy debe permitir el origen de la página en ambas rutas y manejar los métodos/cabeceras requeridos por el preflight del POST. Si el GET de estado está bloqueado, no disponible, no es 2xx o es inválido, el ejemplo trata el trabajo como nuevo y vuelve a publicar cada segmento. Esas escrituras siguen reemplazando los mismos archivos por índice, pero la ejecución pierde sus ahorros de reanudar/saltar. Enruta ambas solicitudes a través del puente GM de la extensión en su lugar si el despliegue no puede proporcionar esa política CORS.

  • Servir subidas almacenadas. El panel enlaza descargas en /uploads/<path>, pero el puente no tiene esa ruta. No sirvas nombres y bytes de subida proporcionados por el llamador directamente en el origen del panel, donde el contenido ejecutable compartiría un origen con el almacenamiento del panel que lleva el token. O deja esos enlaces no disponibles, o haz que /uploads/ redirija a un origen separado solo de descarga que fuerce Content-Disposition: attachment, use application/octet-stream y envíe X-Content-Type-Options: nosniff. GET /upload (listado) y GET /screenshot son servidos por el propio puente y no necesitan ayuda del proxy.

Ejecútalo directamente con un token configurado y ambos simplemente están ausentes — todo lo demás funciona.

Archivos

Archivo

Descripción

extension/manifest.json

Manifiesto MV3

extension/background.js

Service worker — SSE, despacho de comandos, relé de fetch, capturas de pantalla, CDP, cookies, descargas

extension/content.js

Relé de mensajes entre la página y el background

extension/page.js

Puente del mundo MAIN — window.GM + manejador de eval

extension/options.html / options.js

UI de configuración de token/servidor

server.py

Servidor de depuración (también aloja el hilo del daemon MCP en 127.0.0.1:8086)

mcp_server.py

Servidor MCP — conecta la superficie de comandos de la extensión a server.py sobre HTTP

scripts/mcp_probe.py

Cliente MCP mínimo para verificación manual

scripts/check_versions.py

Verificación de consistencia de versiones / incremento en cada sitio de versión

.githooks/

Puertas de consistencia de versiones pre-commit + pre-push

daedalus_cli/

El CLI de shell, publicado como la rueda daedalus-cli

dashboard/

La superficie de control del navegador que server.py sirve en /dashboard

examples/

Scripts para ejecutar en una página mediante put — ver abajo

tests/, run_tests.py

Las suites; python3 run_tests.py ejecuta todas ellas

Ejemplos

examples/ contiene scripts pensados para ejecutarse dentro de una página con put. Cinco de los seis demuestran una parte del puente en lugar de un sitio particular; el de Discord es deliberadamente específico del sitio, porque desplazarse hacia atrás a través de una lista virtualizada es una técnica que no puedes mostrar sin una lista virtualizada real:

Ejemplo

Muestra

request-log-hotfix.js

Un hotfix permanente del mundo MAIN en document_start, y por qué las tres palabras son importantes

hls-segment-relay.js

El relé /segment + /segment-status: reanudable, idempotente, reintento limitado

kill-hls.js

Encontrar y destruir una instancia de reproductor que no dejará de reintentar

react-fill-input.js

Rellenar una entrada controlada por React para que el estado propio de React realmente cambie

scrape-discord-messages.js

Desplazarse hacia atrás a través de una lista de mensajes virtualizada y extraerla

open-tabs.js

La llamada más pequeña posible a GM.openInTab

Toman su configuración mediante sustitución de __PLACEHOLDER__ antes de ser enviados, porque put envía un script en lugar de llamar a una función y no tiene forma de pasar argumentos.

Cada uno es el CUERPO de una función async, no un script independiente: el puente envuelve lo que envías. Por eso la mayoría termina en un return de nivel superior, y uno de ellos — scrape-discord-messages.js — también usa un await de nivel superior; ambos son válidos donde realmente se ejecutan. node --check analiza cada archivo en el envoltorio CommonJS, que permite el return de nivel superior, pero rechaza el await de nivel superior — así que exactamente ese archivo falla la verificación de sintaxis.

Desarrollo

python3 run_tests.py ejecuta todas las suites e imprime una línea agregada que nombra lo que verificó: cuántas suites se ejecutaron, cuántas pruebas pasaron y cuántas se omitieron. Una suite en la que todas las pruebas se omitieron no verificó nada, por lo que se informa como OVERALL: INCOMPLETE con una salida distinta de cero en lugar de como un pase; la causa habitual es una dependencia faltante, y la línea NO COVERAGE encima nombra la suite. Instalar el extra mcp (ver "Instalación") es lo que necesita la suite MCP.

La cadena de versión vive en varios lugares a través de la extensión, el panel y el paquete CLI. python3 scripts/check_versions.py es la lista — nombra cada sitio e informa cuántos encontró, por eso este párrafo no repite un recuento que quedaría obsoleto la próxima vez que se añada uno. Incrementa todos juntos, nunca a mano:

python3 scripts/check_versions.py --set 0.18.0   # rewrite every site
python3 scripts/check_versions.py                # verify the working tree

.githooks/pre-commit verifica el índice y .githooks/pre-push verifica cada commit enviado, así que un incremento a medias no puede aterrizar. Cada clon debe optar una vez — git no ejecutará hooks desde un directorio rastreado de otra manera:

git config core.hooksPath .githooks

Si el navegador carga la extensión desde un checkout diferente al que editas, instala los hooks allí también — y recuerda que recargar la extensión relee los archivos del lado del navegador, no del tuyo.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.
    2 npm
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables browser automation over MCP using a real Chrome browser with existing profile, supporting real tabs, downloads, cookies, and RPA workflows.
    104 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to control real Chrome tabs in existing profiles, supporting navigation, interaction, screenshots, and console/network inspection across multiple Chrome instances.
    -
  • A
    license
    B
    quality
    C
    maintenance
    Enables MCP clients to drive a real Chromium browser for automation, including navigation, JavaScript execution, CDP commands, network capture, and multi-tab control.
    7
    GPL 3.0