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 y captura de red en páginas donde Chrome ejecuta la extensión.

Instalación

Sábelo antes de instalar: la eval normal usa inyección en el mundo MAIN sin banner. Cuando una sonda CSP sin fuente no puede determinar que la compilación dinámica está disponible, Daedalus intenta el fallback con CDP; un adjuntado con éxito muestra el banner de Chrome «Daedalus ha comenzado a depurar este navegador» mientras se ejecuta ese fallback. Una sesión de CDP conservada o una captura de red puede mantener el adjuntado ese tiempo. El banner identifica un adjuntado del 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 y selecciona extension/.

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

Para el raspado paralelo en varias aplicaciones, desactiva la limitación de pestañas en segundo plano de Chrome:

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

Cómo funciona

  1. Token: se genera una sola vez en la instalación mediante crypto.randomUUID() y se guarda en chrome.storage.local.

  2. Tab IDs: la API tabs nativa de Chrome: cada pestaña se identifica por su tabId de Chrome, y se registra al crearla/actualizarla con un latido de 30s usando chrome.alarms.

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

  4. Puente de página: content.js (mundo ISOLADO) retransite mensajes window.GM entre el background y page.js (mundo MAIN). La eval usa primero la inyección chrome.scripting en el mundo MAIN. Cuando una sonda de compilación dinámica sin fuente no establece la disponibilidad, las páginas bloqueadas por CSP se enrutan a CDP; si falla el adjuntaje, se llega al relay de la página. Cada canal ejecuta con la semántica del mundo MAIN de la página.

  5. Hotfixes: se guardan en una clave chrome.storage.local general (daedalus-hotfixes, no por token), se repiten en cada página de nivel superior elegible al cargar y están limitados por versión para los parchees no permanentes. Rotar el token no aísla ni borra este almacén.

Envío de comandos

Daedalus expone su superficie de comandos como servidor MCP en <your-bridge>/mcp (transporte streamable-HTTP). Antes de enviar una petición, exige que el valor Bearer coincida exactamente con el token del puente resuelto mediante la ruta de configuración existente de la CLI: TOKEN anula a DAEDALUS_TOKEN, incluido un proveedor _settings opcional. Sin ningún token configurado, la superficie MCP se cierra con un 401. Agrégalo 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; lo ves en la página de opciones de la extensión (icono de puzle → Daedalus → Opciones).

Ese ejemplo expone el servidor MCP con un hostname público, pero los hosts permitidos por defecto del transporte son solo de loopback (127.0.0.1:*,localhost:*): indica el hostname público en DAEDALUS_MCP_ALLOWED_HOSTS o se rechazan las peticiones proxy. Consulta «Servidor» en el apartado superior para ver los tres ajustes MCP.

40 herramientas en 7 grupos: tabs, eval/debug, media, cookies, CSS/blocking, hotfixes, network/CDP. Consulta la sección <mcp> de CLAUDE.md para la lista completa, o llama a tools/list` en el endpoint MCP.

Ayudante de verificación manual (sin necesidad de 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 una rueda 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 dirigirte a una pestaña concreta (omítelo para transmitir a secas):

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 de exec que envías es una expresión o un cuerpo de función cuyo valor retorna como resultado; consulta «Enviar comandos» en el apartado para el contrato.

También hay una costura de importación opcional: si existe un módulo llamado _settings importable en sys.path, la CLI usa sus funciones setting(name, default) y required(name) para DAEDALUS_URL y DAEDALUS_TOKEN en lugar del cambio de entorno incorporado. La variable de entorno TOKEN sigue siendo la anulación de token puntual y tiene prioridad; sin _settings, DAEDALUS_URL usa su valor predeterminado y DAEDALUS_TOKEN es obligatorio. ID sigue la variable de entorno para la selección transversal.

O publica de forma atómica un archivo de comandos en bruto (sin MCP, sin CLI). La redirección directa al nombre .json final no se puede usar porque el flujo puede observar un archivo antes de que el escritor termine. Escribe un nombre vecino que termine en .tmp y, luego, redirige 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 .tmp vecinos. Si un escritor anterior deja JSON malformado en el nombre final, el lector lo deja sin tocar y reintenta en lugar de borrar una escritura posiblemente en curso. Después del rename atómico, el flujo SSE entrega y consume el comando. El resultado quede en $DAEDALUS_DIR/results/<token>_<tabId>.json (por pestaña) y $DAEDALUS_DIR/results/<token>.json (el último usuario gana). Las respuestas de inyección page-main y page-relay 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 el campo en cualquiera de los canales. Las respuestas CDP no llevan exec_ms. Los resultados asociados a una entrega en cola también llevan roundtrip_ms: el tiempo 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 + relay del cliente + ejecución + retorno, de modo que roundtrip_ms − exec_ms se aproxima a la transmisión / cola cuando ambos campos están presentes. Estas medidas usan tiempos de reloj diferentes: exec_ms usa performance.now() de la página, mientras que roundtrip_ms usa el reloj de pared del servidor en milisegundos, así que la diferencia es aproximada y no una resta exactaV. Un frame legacy sin _did no lleva roundtrip_ms.

Soporte asíncrono

El canal page-main predeterminado ejecuta los expresiones mediante eval de la página y los cuerpos de función con Function de la página; un envoltorio asíncrono lee await en el nivel superior. Antes de enviar el código, el background inyecta solo una sonda Function constante. Si devuelve true, la inyección de código se intenta una sola vez y el resultado es definitivo: se informa un valor, una excepción o un error de transporte de inyección sin reintentar el código por CDP. La página controla Function y pueda influir en esta pista de enrutamiento, pero la sonda no contiene el código enviado, así que elegir otro canal no puede duplicar sus efectos secundarios.

Si la sonda sin fuente no devuelve true, la mayoría de veces porque la CSP de la página bloquea la compilación dinámica, Daedalus intenta con CDP. Cuando se adjunta muestra el banner de depuración de Chrome. Runtime.evaluate usa el modo REPL para el await de que top-level; el código que contenga return se trata como cuerpo de función para una parsi una sonda envoltorio lo analiza como expresión. Esa sonda es una heurística del intérprete, no un límite de seguridad sin ejecución: el personal cualc puede escapulization de él y ejecutarse. Tras el envío de la primera evaluación de origen, todo resultado es definitivo. Solo una falla de adjuntaje o forma avance antes de que el código se ejecute llega al page relay`. El asentamiento de prometidos CDP se limita a 10 segundos, y el mando de objetos de resultado y excepción se libera a pesar de que una sesión o captura mantenida retenga al deprecador.

El código JavaScript evaluado en una página que no controlas devuelve un valor que esa página elige, independientemente del canal que lo ejecute. El campo world solo registra qué canal de ejecución activo; es metadata diagnóstico sobre CSP y comportamiento del depurador, no una señal de confianza. Los valores de world son page-main para las inyecciones normales, cdp para el inspector con fallback y page:<hostname> para el relay final, donde <hostname> es el location.hostname del script de contenido. El background añade el prefijo page:, así que el hostname del relay no puede entrar en cdp ni page-main: esto no dice nada sobre el valor devuelto. La CLI muestra ese campo como channel=..., el panel hace lo mismo y MCP exec, put, result y ping presionan el valor exacto de world. Ninguna de ellas asigna una clase de confianza.

Las inyecciones por defecto y el fallback CDP mantienen el comportamiento clásico de script para los casos documentados de los scripts no estrictos: with compila, los literales octales legales se aprovechan y una asignación sin introducción crea un global. El modo REPL de CDP también permite let y const repetidos. La espera de resultado CLI y MCP es de 15 segundos por defecto; un timeout del llamador no cancela el código que ya se ejecuta. El Sep relay espera por separado hasta 10 segundos por un código que contenga await, y 3 segundos en caso contrario, antes de informar timeout de fallback.

La correlación del relay queda sin filosofía y limitada: la pestaña del emisor debe coincidir, un mensaje aceptado consume el id aleatorio del relay, y no se registra ningún id hasta la sonda de inyección sin fuente y el fallback de CDP anterior a los dicho hayan terminado. Estos controles evitan la finalización transversal o duplicada del relay; no establecen integridad de valor.

Máximo hay 1.000 entradas de relay de página. Un nuevo fallback al límite de la capacidad recibe un único error de capacidad, sin desalojar trabajo en curso. Cada entrada registrada vence tras 300.000 ms y recibe un único error de timeout si se no la elimina una finalización de la misma pestaña o un fallo de envío anterior.

Panel de control

Una superficie de control en el navegador en <your-bridge>/dashboard, que controla todo el conjunto de ordenes sin la CLI: lista de aplicaciones en vivo, REPL de eval, capturas de pantalla, cookies, hotfixes, reglas de bloque, captura de red, CDP, inyección CSS, tiempos de fetch, explorador de carga.

  1. Abre la URL en una pestaña que la extension controle.

  2. Despláate a §12 Configurar y pega el token de la pagina de opciones de la extensión (icono de puzle → Daedalus → Opciones). Guarda.

  3. El punto de estado SSE en la barra en la parte superior se vuelve cian cuando el ícono o dashboard está viendo eventos live.

Servido directamente por server.py desde el directorio dashboard/ del repositorio (JavaScript vanilla + módulos ES, sin build). Las actualizaciones en vivo llegan por entpo punto /stream: server.py coloca eventos en la cola de commands/<token>_dashboard/<ts>_<uuid>.json cuando /register actualiza una pestaña existente, y en los métodos pathways de /sync-tabs, /unregister y /result; /unregister envía incluso cuando no hay pestaña. El dashboard evacúa si kind:'event'.

Aviso: los scripts de contenido y de página de la extensión se inyectan en las páginas que coinciden, incluyendo la del dashboard. Los comandos de eval en difusión (exec -b) corren también una predefinida aplicación; mejor usa el targeting por pestañas o cierra el panel antes de emitir código disruptivo.

Auto-parche

Hotfixes

Persistencia de pequeños parches que se repiten en cada argumento de página elegible que la carga. Por medio de MCP: store_hotfix, list_hotfixes, clear_hotfix, clear_hotfixes, set_permanent. Ejemplo (mediante script probe):

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 se controlan por versión por defecto. Cuando cambie la versión de la extensión, las correcciones restantes no permanentes se omiten en vez de borrarse; guardar una corrección actualiza el registro de la versión actual y activa hacia arriba otras no permanentes. Marca una corrección como permanente (con permanent: true o set_permanent) para que se repita usen cambios de versión. clear_hotfixes elimina las no permanentes y deja las permanentes por defecto; usa include_permanent: true para borrar todo el almacén completo.

Comandos de la extensión

El service worker del background acepta comandos con tipo (enviados por MCP o escribiendo un JSON {"type": "python"} en $DAEDALUS_DIR/commands/):

Comando

Propósito

screenshot

Capturar la pestaña visible como PNG

cdp

Emitir llamadas directas al protocolo Chrome DevTools Protocol

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

Interceptación completa de solicitudes/respuestas mediante CDP

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

Acceso al depósito 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

GM Bridge

window.GM (en page.js, mundo MAIN) proporciona este subconjunto de 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()

Enumerar las claves de almacenamiento no reservadas

GM.xmlhttpRequest(opts)

Realizar una petición HTTP retransmitida en segundo plano (inmune a CSP)

GM.addStyle(css)

Inyectar CSS

GM.setClipboard(text, type)

Escribir en el portapapeles

GM.notification(opts)

Mostrar una notificación de escritorio

GM.openInTab(url, opts)

Abrir una nueva pestaña

GM.download(opts)

Iniciar una descarga

GM.info

Metadatos del script

El acceso a las cookies es una capacidad del operador, no de la página: se realiza a través de los comandos cookies / set-cookie / remove-cookie / clear-cookies autenticados por token que se han indicado más arriba y, deliberadamente, no se expone al contexto de la página: page.js se ejecuta en cada página de nivel superior coincidente, 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 proceso de 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 30 s fuerza la reconexión cuando los flujos quedan obsoletos.

  • Enrutamiento por pestaña: los comandos se dirigen a un tabId concreto de Chrome o se transmiten a todas las pestañas.

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

Servidor

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

server.py se ejecuta bajo cualquier supervisor que uses (aquí, una unidad de systemd). Coloca un proxy inverso que término de TLS delante s tu exposición si lo expones más allá de localhost; el propio bridge habla HTTP plano. Las rutas de control y de almacenamiento del bridge comparan su token con un secreto configurado, resuelto mediante la ruta de configuración de la CLI: TOKEN es el valor de anulación puntual, y si no se establece, DAEDALUS_TOKEN es obligatorio (y un módulo _settings integrado puede proporcionarlo). La falta de configuración y los desajustes bloquean en modo seguro. Solo las rutas orientadas a la página POST /segment y GET /segment-status utilizan una capacidad con ámbito de trabajo en su lugar.

El etc. frontend MCP en ei. Acepta tres ajustes opcionales, documentados juntos porque describen un único listener: DAEDALUS_MCP_PORT (por defecto 8086) es el puerto de loopback al que se vincula; el handler de tools usa la URL de loopback realmente, incluso cuando DAEDALUS_PORT=0; DAEDALUS_LOCAL_URL anula explícitamente esa URL para un despliegue MCP independiente que pon delante de un bridge que se ejecuta en otro lugar; y DAEDALUS_MCP_ALLOWED_HOSTS (por defecto 127.0.0.1:*,localhost:*) es la lista de hosts permitidos por separado de comas que la protección contra rebinding de DNS acepta, así que exponer /mcp con un nombre de host público significa nombrar ese host ahí.

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 de bridge configurado; solo POST /segment y GET /segment-status aceptan el sig de ámbito de job. Se CLAUDE.md para notas sobre el payload y los endpoints.

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

PUT /command añade a una cola FIFO de directorio por destino (los comandos consecutivos contra la misma pestaña ya no se sobrescriben), sella cada uno con un identifice de entrega (_did) para que a extensión pueda deducir un frame reenviado, y caduca por TTL los comandos no reclamados después de DAEDALUS_CMD_TTL segundos (por defecto 90). Un recolector ejecuta ese TTL y vacía los diretorios de cola vacíos incluso si ningún consumidor SSE llega a conectarse. GET /health informa del estado de stream/registro/última entrega para detectar un bridge muerto silenciosamente.

El bridge rechaza las entradas de autoridad authority repetidas en lugar de elegir un valor: esto incluye token en las cadenas de consulta o en los cuerpos JSON y job / sig en las rutas de taller de segmento, incluso si los valores repetidos son iguales o están vacíos. El transporte MCP también rechaza los campos de cabecera Authorization, Mcp-Session-Id, Host y Origin, además de los argumentos job repetidos para las herramientas de segmento.

Cuando la extensión publica un resultado, el servidor expone el campo _did del comando como deliveryId y asigna un nuevo resultGeneration. Los clientes de CLI y MCP que esperan, primero miran en la ranura de resultado compartida, comprueban el ID de comando y el deliveryId, y después consumen condicionalmente esa generación con GET /result?...&consume=1&expected=<resultGeneration>. Si otro resultado sustituye a la ranura entre esas solicitudes, la consumición condicional deja el nuevo resultado en su lugar y reporta que no se ha consumido. La opción consume=1 sin expected sigue siendo una lectura destructiva de compatibilidad de la ranura actual.

GET /stream mantiene la conexión SSE abierta indefinidamente, probando la vivacidad con un comentario keepalive cada DAEDALUS_STREAM_KEEPALIVE segundos (por defecto 15) en lugar de reciclar la conexión con un temporizador; DAEDALUS_STREAM_MAX_AGE (por defecto 3600) es solo un techo de último recurso. El camino de cierre queda anclado por tests/test_stream_lifecycle.py.

DAEDALUS_MAX_BODY_SIZE (por defecto 64 * 1024 * 1024 bytes, 64 MiB) límites los cuerpos de solicitud que minimizan los handlers POST, PUT y DELETE; cuando el cuerpo declarado supera el límite, recibe 413. Aumenta el valor cuando transmites segmentos u otros payloads más grandes.

Los valores del llamador respaldados por el sistema de ficheros usan una política de un componente de ruta: rechaza .., caracteres de control C0/C1 y surrogates, caracteres no, no válidos en rutas de Windows y nombres de dispositivos, puntos o espacios traseros, y codificaciones UTF-8 más largas de 240 bytes. Los tokens de bridge son más estrictos: también se rechazan puntos y guiones bajos. Otros nombres de job en UTF-8 son aceptados, por lo que los clientes deben codificarlos en la URL query.

POST /segment-job pone tres límites fijos en cada nuevo registro de job: DAEDALUS_MAX_SEGMENT_INDEX (por defecto 99999), `DAEDALUS_MAX_SEGMENTS_PER_JOB

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

Los resultados de evaluación no ofrecen ninguna 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, sea cual sea el canal que lo ejecute. 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, así que ningún hostname puede producir cdp ni page-main; esa resistencia a la colisión es solo descriptiva. En page-main, los enlaces eval y Function propiedad de la página pueden leer el código fuente enviado y también influir en el valor que devuelve.

La compilación de cdp evita resolver los enlaces eval y Function de la página, y la implementación obtiene los manejadores directos por referencia antes de serializarlos. Esos mecanismos de transporte no cambian el límite de confianza: el código fuente enviado sigue leyendo estado controlado por la página y puede encaminar cualquier valor, incluso 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 fuente enviado use esas rutas controladas por la página, también en cdp la página puede elegir el valor devuelto.

El relé solo acepta la invocación almacenada asociada a ese identificador aleatorio, exige que la pestaña del remitente coincida y consume la entrada una sola vez. Esta degradación no expone el token del puente, la URL del servidor, el id de envío ni la ruta de resultado; no otorga autoridad adicional de la extensión ni del navegador, y no puede afectar a una invocación en otra pestaña. Esas propiedades protegen el encaminamiento 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 el bucle local. Las rutas operación de control y de almacenamiento requieren el token configurado y fallan de forma segura si no está presente; solo /segment y /segment-status usan la capacidad asociada al trabajo. Hay dos cosas que deliberadamente NO hace, porque le corresponden a lo que tenga delante:

  • TLS y CORS. server.py no envía cabeceras CORS. Cuando el puente está en origenes cruzados, las llamadas fetch del lado pagina del ejemplo del relé HLS necesitan acceso tanto a GET /segment-status como a POST /segment; el proxy debe permitir el origen de la página en ambas rutas y gestionar los métodos y cabeceras que exige el preflight del POST. Si el GET de estado está bloqueado, no está disponible, devuelve un resultado no 2xx o no es válido, el ejemplo trata trabajo como nuevo y vuelve a hacer POST de cada segmento. Esas escrituras siguen reemplazando los mismos ficheros por índice, pero la ejecución pierde el ahorro de la retoma/omisión. Si el despliegue no puede ofrecer esa política de CORS, pasa ambas solicitudes por el puente GM de la extensión.

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

Si lo ejecutas directamente con un token ya configurado, esas dos cosas simplemente no existen: todo lo demás funciona.

Archivos

Archivo

Descripción

extension/manifest.json

Manifiesto MV3

extension/background.js

Service worker — SSE, envío 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

Interfaz de configuración de token / servidor

server.py

Servidor de depuración (también sirve 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 con server.py por HTTP

scripts/mcp_probe.py

Cliente MCP mínimo para verificación manual

scripts/check_versions.py

Comprueba/aplica cambios de consistencia de versión en todos los puntos

.githooks/

Puertas de consistencia de versión pre-commit y pre-push

daedalus_cli/

El CLI shell, publicado como rumbo de daedalus-cli

dashboard/

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

examples/

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

tests/, run_tests.py

Los grupos de pruebas; python3 run_tests.py los ejecuta todos

Ejemplos

En examples/ guardan scripts pensados para ejecutarlas dentro de una página con put. Cinco de los seis muestran una parte del puente más que de un sitio concreto; el de Discord es deliberadamente específico de sitio, porque desplazarse hacia atrás en una lista virtualizada es una técnica que no se puede mostrar sin una lista virtualizada real:

Ejemplo

Muestra

request-log-hotfix.js

Un parche permanente del mundo MAIN en document_start, y por qué esas tres palabras son esenciales

hls-segment-relay.js

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

kill-hls.js

Encontrar y eliminar una instancia del reproductor que no deja de reintentar

react-fill-input.js

Rellenar un campo controlado por React para que el estado de React cambia de verdad

scrape-discord-messages.js

Desplazarse hacia atrás por una lista virtualizada de mensajes y extraer los datos

open-tabs.js

La llamada GM.openInTab más mínima posible

Reciben su configuración mediante sustitución de __PLACEHOLDER__ antes de enviarlas, porque put es un script que se envía y no tiene forma de pasar argumentos.

Cada una es el CUERPO de una función asíncrona, no script independiente: el puente envuelve lo que envías. Por eso la mayoría acaban en return de nivel superior, y una — scrape-discord-messages.js — también usa un await de nivel superior; ambos son válidas en el contexto real en que se ejecutan. node --check analiza cada archivo dentro del envoltorio CommonJS, que permite el return de nivel superior, pero rechaza el await de nivel superior; por tanto, ese archivo o normal falla la comprobación de sintaxis.

Desarrollo

La cadena de versión vive catre sitios varios por toda la extensión, en elpanel y en el paquete CLI. python3 scripts/check_versions.py es la lista: nombra cada lugar y dice cuántos ha encontraado, que es por lo que este párrafo no repite un total que quedaría desfasado en cuanto se añada otro. suben hacia arriba, all together, never by manual:

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

.githooks/pre-commit comprueba que el índice está entero, y .githooks/pre-push comprueba cada commit empujado para que no pueda aterrizar una subida a medio cerrarse. Cada clon debe suscribirse una vez — git no va a ejecutar los hooks si están en un directorio quegit ya tiene solo así:

git config core.hooksPath .githooks

Si el navegador carga la extensión desde otro checkout distinto del que vas a modificar, instala también los hooks allí; y recuerda que recargar la extensión lee los ficheros efectos del navegador, no los tuyos.

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.

  • Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.

  • Access Kernel's cloud-based browsers and app actions via MCP (remote HTTP + OAuth).

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Nitjsefnie-Harness-Commons/daedalus'

If you have feedback or need assistance with the MCP directory API, please join our Discord server