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:12export 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:481page.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