Skip to content

BrowserToolHandler: automatización de navegador con Puppeteer ​

源码版本v4.0.10

Responsabilidades ​

BrowserToolHandler es la entrada de Cline para operar el navegador, correspondiente al nombre de herramienta ClineDefaultTool.BROWSER. El modelo usa el parámetro action para elegir la operación concreta: launch (abrir URL), click (clic por coordenadas), type (escribir texto en el foco actual), scroll_up/scroll_down (desplazar la página), close (cerrar el navegador) (class declaration:12-13). Tras cada acción devuelve una captura de pantalla y los console logs de esa ventana, para que el modelo entienda el estado de la página visualmente.

Es solo una «fachada»: al recibir un tool_use (llamada a herramienta) hace validación de parámetros, aprobación, hook, y luego delega la acción a config.services.browserSession, una instancia de BrowserSession. El proceso real del navegador, la API de Puppeteer, la codificación de capturas y la conexión a navegador remoto viven en BrowserSession (class BrowserSession:38). BrowserSession usa puppeteer-core para conectar a Chrome local o remoto, y obtiene los objetos Browser/Page vía connect/launch.

Dentro de Cline esta herramienta es una «herramienta de capacidad (capability tool)», al mismo nivel que file ops y web fetch: todas son unidades de ejecución one-shot levantadas por ToolExecutor. Lo particular es que tiene estado: dentro de un Task solo se abre una sesión de navegador, y tras launch todos los click/type/scroll posteriores reutilizan el mismo Page, hasta close o hasta un error. Por eso el handler llama closeBrowser() en cualquier rama de error: el proceso del navegador no puede quedar residual.

Motivación de diseño ​

  • Reutilización de sesión única: tras launch el Page se reutiliza hasta close, evitando reabrir en cada click; el modelo también mantiene login y posición de scroll entre acciones (launchBrowser:155).
  • Doble canal captura + logs de consola: tras cada acción, doAction espera 500 ms de silencio en consola y luego captura, devolviendo visión + logs al modelo, compensando el hueco donde la visión pura no captura errores JS (doAction:378).
  • Aprobación separada para launch: abrir navegador es de alto riesgo, launch pasa por un ask independiente tipo browser_action_launch; las demás acciones (click/type/scroll) se autoprueban por defecto si launch ya fue aprobado (launch flow:81).
  • Fallback a navegador remoto: si la conexión remota falla, se degrada a modo local para no hundir toda la herramienta (remote fallback:181-198).
  • Tras click, esperar navegación: un click puede disparar salto de página; doAction escucha el evento request en click para detectar actividad de red y, si la hay, llama waitForNavigation (click:528-561).
  • Forzar cierre del navegador antes de cambiar de herramienta: el resultado incluye un recordatorio «REMEMBER ... debes cerrar el navegador con close antes de usar herramientas que no sean browser_action», para que el modelo no cambie a editar archivos con el navegador abierto (result reminder:190-194).

Archivos clave ​

  • class declaration:12 — export class BrowserToolHandler implements IFullyManagedTool, name = ClineDefaultTool.BROWSER.
  • handlePartialBlock:19 — rama de streaming: launch pasa por ask browser_action_launch, las demás acciones solo say con coordenadas/texto actuales.
  • execute:62 — flujo principal: validar action → rama launch o rama click/type/scroll/close → devolver screenshot + logs.
  • launch flow:81-127 — flujo completo de launch: validar url, aprobación, PreToolUse hook, applyLatestBrowserSettings refresca session, launchBrowser + navigateToUrl.
  • click/type param check:130-145 — cuando click falta coordinate o type falta text, cada uno reporta error y closeBrowser.
  • action dispatch:163-179 — switch que reparte a browserSession.click/type/scrollDown/scrollUp/closeBrowser.
  • result format:184-203 — launch/click/type/scroll devuelven screenshot + logs, close devuelve texto plano.
  • error close:205-208 — cualquier excepción llama closeBrowser, sin dejar proceso Chrome residual.
  • launchBrowser:155 — remoto primero, fallback local, luego browser.newPage() abre una pestaña nueva.
  • doAction:378 — cuelga listeners de console/pageerror, corre la acción, espera silencio 500 ms, captura, limpia listeners.
  • navigateToUrl:481 — page.goto con domcontentloaded + networkidle2, luego waitTillHTMLStable sondea el tamaño del HTML.
  • click:528 — descompone coordenadas, page.mouse.click, escucha request para decidir si espera navegación.
  • screenshot:441 — webp primero, fallback png si falla, lanza error y manda telemetría si ambos fallan.

Flujo de datos ​

La rama launch es el flujo más completo de BrowserToolHandler: validar url → aprobación → hook → refrescar browserSession → abrir navegador → navegar → devolver captura y logs. Código clave:

typescript
// apps/vscode/src/core/task/tools/handlers/BrowserToolHandler.ts
if (action === "launch") {
    if (!url) {
        config.taskState.consecutiveMistakeCount++
        const errorResult = await config.callbacks.sayAndCreateMissingParamError(this.name, "url")
        await config.services.browserSession.closeBrowser()
        return errorResult
    }
    config.taskState.consecutiveMistakeCount = 0
    // ... approval + PreToolUse hook ...
    await config.callbacks.say("browser_action_result", "")
    config.services.browserSession = await config.callbacks.applyLatestBrowserSettings()
    await config.services.browserSession.launchBrowser()
    browserActionResult = await config.services.browserSession.navigateToUrl(url)
}

Ojo con config.services.browserSession = await config.callbacks.applyLatestBrowserSettings(): no solo refresca la configuración, también reemplaza la referencia a browserSession que tiene ToolExecutor, de modo que las siguientes llamadas browser tool obtengan la misma sesión nueva (applyLatestBrowserSettings:125).

navigateToUrl pasa por doAction, y todas las acciones que no son launch también pasan por doAction. doAction combina captura + logs de consola en un BrowserActionResult:

typescript
// apps/vscode/src/services/browser/BrowserSession.ts
const screenshotType = this.useWebp ? "webp" : "png"
let screenshotBase64 = await this.page.screenshot({ ...options, type: screenshotType })
let screenshot = `data:image/${screenshotType};base64,${screenshotBase64}`
if (!screenshotBase64) {
    screenshotBase64 = await this.page.screenshot({ ...options, type: "png" })
    screenshot = `data:image/png;base64,${screenshotBase64}`
}
return {
    screenshot,
    logs: logs.join("\n"),
    currentUrl: this.page.url(),
    currentMousePosition: this.currentMousePosition,
}

El handler envuelve este result en formatResponse.toolResult y adjunta el recordatorio fuerte de «close el navegador para volver a otras herramientas».

Límites y fallos ​

  • action ilegal: al completar el block, si action no está en la lista browserActions, consecutiveMistakeCount++ + sayAndCreateMissingParamError + closeBrowser (invalid action:69-75).
  • launch sin url: launch debe llevar url; si falta, mismo mistake++ y closeBrowser (missing url:82-87).
  • click sin coordinate / type sin text: parámetro faltante, se reporta error y closeBrowser, sin dejar sesión residual (param checks:131-145).
  • Falla conexión a navegador remoto: cuando launchRemoteBrowser lanza, el catch degrada a launchLocalBrowser y la telemetría registra remote_browser_launch_error (remote fallback:181-198).
  • Captura fallida: si webp falla, reintenta con png; si ambos fallan, lanza "Failed to take screenshot." y manda telemetría screenshot_error (screenshot retry:448-466).
  • page no iniciada: doAction verifica si this.page no existe y lanza error con «Browser is not launched»; suele ocurrir cuando una herramienta no browser_action (en realidad sin permiso) o tras un close previo se vuelve a llamar (page check:379-383).
  • Cualquier excepción cierra el navegador: el try/catch del handler siempre cae a browserSession.closeBrowser(), garantizando que no quede un proceso Chrome consumiendo recursos (error close:205-208).

Resumen ​

BrowserToolHandler es una cáscara fina; quien realmente trabaja es BrowserSession. El modelo elige launch/click/type/scroll/close vía el parámetro action, el handler valida parámetros y aprobación, delega la acción a las llamadas Puppeteer de BrowserSession y devuelve la captura y los logs de consola empaquetados al modelo. launch se aprueba aparte; las demás acciones reutilizan la sesión abierta; cualquier error fuerza el cierre del navegador.

Para profundizar:

  • Herramientas de archivos y comandos: /cap-tools/file-ops
  • Web fetch/search: /cap-tools/web
  • Cadena de despacho de herramientas: /agent-loop/task-class

Véase la documentación oficial: Cline 文档 · README