BrowserToolHandler:Puppeteer 瀏覽器自動化
職責
BrowserToolHandler 是 Cline 操作瀏覽器的入口,對應 ClineDefaultTool.BROWSER 工具名。模型用 action 參數選具體操作:launch(開啟 URL)、click(按座標點擊)、type(在目前焦點輸入文字)、scroll_up/scroll_down(捲動頁面)、close(關閉瀏覽器)(class declaration:12-13)。每個動作執行後都會回傳一張截圖和這段視窗裡的 console logs,讓模型用視覺理解頁面狀態。
它本身只是「門面」:接到 tool_use 後做參數校驗、審批、hook,然後把動作轉交給 config.services.browserSession 這個 BrowserSession 實例。真正的瀏覽器行程、Puppeteer API、截圖編碼、遠端瀏覽器連線都在 BrowserSession 裡(class BrowserSession:38)。BrowserSession 用 puppeteer-core 連接本地 Chrome 或遠端瀏覽器,透過 connect/launch 拿到 Browser/Page 物件。
這套工具在 Cline 裡的位置是「能力類工具 (capability tool)」,和 file ops、web fetch 同級,都是被 ToolExecutor 拉起來的一次性執行單元。它特殊的地方在於有狀態:一個 Task 裡瀏覽器工作階段只開一個,launch 後所有後續 click/type/scroll 都沿用同一個 Page,直到 close 或出錯。這也是為什麼 handler 在任何錯誤分支都調 closeBrowser() —— 瀏覽器行程不能殘留。
設計動機
- 單工作階段沿用:launch 後 page 沿用到 close,避免每次 click 都重新開啟,模型也能在多次 action 間保持登入狀態、捲動位置(
launchBrowser:155)。 - 截圖 + 控制台日誌雙通道:每個 action 完成後,
doAction等控制台靜默 500ms 再截圖,把視覺 + 日誌一起回給模型,彌補純視覺無法捕獲 JS 錯誤的缺口(doAction:378)。 - launch 單獨審批:開瀏覽器是高風險動作,launch 走
browser_action_launch這種獨立的 ask 類型,其他 action (click/type/scroll) 只要 launch 已批准就預設通過(launch flow:81)。 - 遠端瀏覽器兜底:遠端瀏覽器連線失敗時降級到本地模式,不讓整個工具崩(
remote fallback:181-198)。 - click 後等導航:點擊可能觸發頁面跳轉,doAction 在 click 時監聽
request事件判斷是否有網路活動,有就waitForNavigation(click:528-561). - 強制關瀏覽器才能換工具:回傳結果裡附一句「REMEMBER ... 必須先 close 瀏覽器才能用非 browser_action 工具」,提醒模型別在瀏覽器開著時切去改檔案(
result reminder:190-194)。
關鍵檔案
class declaration:12—export class BrowserToolHandler implements IFullyManagedTool,name = ClineDefaultTool.BROWSER。handlePartialBlock:19— 串流分支:launch 走browser_action_launchask,其他 action 直接 say 目前座標/文字。execute:62— 主流程:校驗 action → launch 分支或 click/type/scroll/close 分支 → 回傳 screenshot + logs。launch flow:81-127— launch 的完整流程:校驗 url、審批、PreToolUse hook、applyLatestBrowserSettings刷新 session、launchBrowser+navigateToUrl。click/type param check:130-145— click 缺 coordinate、type 缺 text 時各自報錯並 closeBrowser。action dispatch:163-179— switch 分發到browserSession.click/type/scrollDown/scrollUp/closeBrowser。result format:184-203— launch/click/type/scroll 回傳 screenshot + logs,close 回傳純文字提示。error close:205-208— 任何例外都closeBrowser,不殘留瀏覽器行程。launchBrowser:155— 遠端優先,失敗降級本地,然後browser.newPage()開新 tab。doAction:378— 掛 console/pageerror 監聽、跑 action、等靜默 500ms、截圖、清理 listener。navigateToUrl:481—page.goto用domcontentloaded + networkidle2,再waitTillHTMLStable輪詢 HTML 大小。click:528— 拆座標、page.mouse.click、監聽 request 決定是否等導航。screenshot:441— webp 優先,失敗兜底 png,都失敗拋錯並打遙測。
資料流
launch 分支是 BrowserToolHandler 最完整的流程:校驗 url → 審批 → hook → 刷新 browserSession → 開瀏覽器 → 導航 → 回傳截圖與日誌。關鍵程式碼:
// 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)
}注意 config.services.browserSession = await config.callbacks.applyLatestBrowserSettings() 這一行:它不只刷新設定,還把 ToolExecutor 持有的 browserSession 引用替換掉,這樣後續 browser tool 呼叫拿到的才是同一個新 session(applyLatestBrowserSettings:125)。
navigateToUrl 內部走 doAction,所有非 launch action 也都走 doAction。doAction 把截圖 + 控制台日誌組合成 BrowserActionResult:
// 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,
}handler 把這個 result 包成 formatResponse.toolResult 回傳,並附上那段強提醒「close 瀏覽器才能切回其他工具」。
邊界與失敗
- action 非法:block 完成時 action 不在
browserActions列表裡,consecutiveMistakeCount+++sayAndCreateMissingParamError+closeBrowser(invalid action:69-75)。 - launch 缺 url:launch 必須帶 url,缺了同樣 mistake++ 和 closeBrowser(
missing url:82-87)。 - click 缺 coordinate / type 缺 text:對應參數缺失,報錯並 closeBrowser,不讓 session 殘留(
param checks:131-145)。 - 遠端瀏覽器連線失敗:
launchRemoteBrowser拋錯時,catch 後降級到launchLocalBrowser,遙測記錄 remote_browser_launch_error(remote fallback:181-198)。 - 截圖失敗:webp 失敗時切 png 重試,兩次都失敗拋 "Failed to take screenshot." 並打 screenshot_error 遙測(
screenshot retry:448-466)。 - page 未啟動:
doAction檢查this.page不存在時拋錯,提示「Browser is not launched」,這種情況通常是 非 browser_action 工具 (其實沒有權限) 或前次 close 後又調(page check:379-383)。 - 任何例外都關瀏覽器:handler 的 try/catch 兜底是
browserSession.closeBrowser(),保證不會留一個 Chrome 行程佔資源(error close:205-208)。
小結
BrowserToolHandler 是個薄殼,真正幹活的是 BrowserSession。模型透過 action 參數選 launch/click/type/scroll/close,handler 做參數校驗和審批,把動作轉給 BrowserSession 的 Puppeteer 呼叫,然後把截圖和 console 日誌打包回給模型。launch 單獨審批,其他 action 沿用已開的工作階段;任何錯誤都強制關瀏覽器。
想往裡鑽可以接著看:
- 檔案與命令類工具:
/cap-tools/file-ops - Web fetch/search:
/cap-tools/web - 工具調度鏈路:
/agent-loop/task-class