1. 项目概述为AI智能体装上“眼睛”和“手”如果你正在使用Claude Code、Cursor这类AI编程助手或者任何需要与网页交互的AI Agent你肯定遇到过这样的困境AI的代码写得头头是道但一到实际执行比如让它“去这个网站查一下价格”、“点击那个按钮登录”、“把表格数据抓下来”就彻底卡壳了。AI本身没有浏览器它无法“看到”网页更无法“操作”网页。传统的解决方案要么是让AI生成Playwright或Puppeteer脚本然后你手动去跑要么是集成一些复杂的MCPModel Context Protocol服务器或Chrome扩展配置繁琐安全性也让人捏把汗。dev-browser的出现就是为了干净利落地解决这个问题。它本质上是一个命令行工具也是一个Claude Skill其核心使命是为AI智能体提供一个安全、可控的浏览器自动化执行环境。你可以把它理解为AI的“远程手眼系统”AI负责思考和发出指令“去谷歌搜索点击第一个结果”而dev-browser则负责忠实地、安全地执行这些指令并将结果页面标题、截图、抓取的数据反馈给AI。它的设计哲学非常明确安全第一简单至上。所有由AI生成并交给dev-browser执行的JavaScript脚本都被运行在一个基于QuickJS WASM的沙箱环境中。这个沙箱与你的主机系统完全隔离脚本无法访问你的本地文件系统除了一个专用的临时目录、无法发起任意网络请求、更无法执行系统命令。这意味着即使AI不小心或被诱导生成了恶意代码风险也被牢牢控制在沙箱内。同时它的使用方式极其简单安装后AI只需要学会调用dev-browser这个命令就能获得几乎完整的Playwright浏览器操控能力。2. 核心设计解析安全与集成的平衡术2.1 沙箱化执行为什么是QuickJS WASMdev-browser最核心的安全基石就是其脚本执行环境。它没有选择Node.js而是采用了QuickJS 引擎编译为 WebAssembly (WASM)的方案。这是一个非常巧妙且务实的选择。安全性考量Node.js功能强大但也意味着风险极高。一个拥有require能力的脚本几乎可以对你电脑做任何事情。QuickJS本身是一个轻量级、可嵌入的JavaScript引擎其WASM版本运行在一个严格的内存沙箱中。dev-browser在此基础上对运行时环境进行了精心的“裁剪”和“限制”全局对象隔离只暴露有限的、安全的API如browser、console、以及受限的文件IO函数readFile/writeFile路径被锁定在~/.dev-browser/tmp/。无模块系统脚本中无法使用require或import来加载任何外部模块从根本上杜绝了依赖注入攻击。无宿主环境访问脚本无法获取process、BufferNode.js、window、document浏览器等对象切断了所有通往外部世界的非授权通道。性能与兼容性对于浏览器自动化任务脚本逻辑通常不复杂定位元素、点击、填写、提取数据QuickJS的性能完全足够。更重要的是它提供了足够标准的ES2020 JavaScript语法支持让AI生成的代码能顺畅运行。选择WASM格式也使得dev-browser的核心引擎可以跨平台Windows, macOS, Linux一致地部署和运行。实操心得这种沙箱设计带来的一个直接好处是你可以相对放心地让AI去操作一些需要登录的网站比如公司内网、测试环境而不用担心密码或Session令牌被脚本窃取并发送到外部服务器。因为脚本根本没有网络发送能力除非通过你授权的浏览器页面本身。2.2 与Playwright的深度集成能力来源安全沙箱解决了“能不能放心用”的问题而强大的浏览器操控能力则解决了“好不好用”的问题。dev-browser并没有重复造轮子去实现一套浏览器控制协议而是选择了业界标杆Playwright作为其能力底层。能力透传dev-browser启动或连接一个真实的Chrome/Chromium浏览器实例。当你在脚本中通过browser.getPage()获取到一个页面对象后这个对象就是一个真正的、完整的Playwright Page对象。这意味着AI可以利用Playwright所有强大的API导航page.goto(url, options)交互page.click(selector),page.fill(selector, text),page.selectOption(...)等待与定位page.waitForSelector(selector),page.locator(selector).first(),page.locator(button).getByText(Submit)评估与抓取page.evaluate(() document.title),page.textContent(selector)截图与PDFpage.screenshot(options),page.pdf(options)特别是page.snapshotForAI()方法这是为AI场景量身定做的利器。它返回一个结构化的页面快照包含清理后的HTML、可访问的文本内容以及元素位置等信息格式非常适合大语言模型LLM消化和理解当前页面状态从而做出下一步决策。这比让AI去解析原始HTML或截图OCR要高效和准确得多。连接模式灵活性dev-browser支持两种工作模式启动模式 (--headless)自动启动一个全新的、纯净的Chromium浏览器实例。适合一次性任务或需要完全独立环境的场景。连接模式 (--connect)连接到你已经打开的、开启了远程调试--remote-debugging-port9222的Chrome浏览器。这个功能极其有用因为它允许AI操作你正在使用的浏览器。比如你可以手动登录某个复杂认证的网站然后让AI接管后续的重复性操作。2.3 面向AI Agent的交互设计dev-browser的CLI设计充分考虑了AI的使用习惯。它不像一个给人用的复杂工具而更像一个为AI准备的、功能明确的“远程过程调用RPC端点”。标准输入STDIN驱动这是最核心的交互模式。AI只需要将生成的JavaScript脚本通过标准输入传递给dev-browser命令即可。例如在Bash中echo “脚本内容” | dev-browser。这使得任何能执行Shell命令的AI环境Claude Code, Cursor, Windsurf等都能轻松集成。自描述的Help系统运行dev-browser --help会输出一份结构清晰、包含大量示例的文档。这份文档本身就是为LLM优化的AI可以读取它来学习如何使用这个工具包括API签名、参数说明和典型用例。这实现了“零样本”或“少样本”学习AI无需额外的插件训练就能掌握其用法。持久化页面Named Pagesbrowser.getPage(“main”)这个API设计得很巧妙。通过给页面一个名字如“main”多个独立的脚本可以共享和操作同一个浏览器标签页。这对于需要多步交互的复杂任务至关重要。AI可以在第一个脚本中打开页面并登录在第二个脚本中执行查询在第三个脚本中保存结果它们操作的都是同一个“main”页面状态得以保持。3. 从零开始的完整实操指南3.1 环境准备与安装dev-browser的安装过程力求简洁但不同平台仍有细微差别。macOS / Linux 安装在终端中执行以下命令即可。npm install -g会下载dev-browser的CLI工具而dev-browser install这个后置命令会自动为你安装Playwright所需的浏览器二进制文件通常是Chromium。npm install -g dev-browser dev-browser install注意dev-browser install这一步可能会需要下载几十到上百MB的浏览器文件请确保网络通畅。如果遇到权限问题在Linux/macOS上可能需要使用sudo来运行全局安装命令但更推荐的方式是配置npm的全局安装目录到用户有权限的路径。Windows 安装在PowerShell或CMD中安装步骤是相同的。但由于Windows的进程管理和二进制分发机制不同dev-browser在Windows上采用了一种混合模式。npm install -g dev-browser dev-browser install安装过程中npm会从GitHub Releases下载一个名为dev-browser-windows-x64.exe的预编译原生可执行文件。之后当你运行dev-browser命令时npm生成的shim脚本会直接调用这个原生可执行文件以获得更好的性能和兼容性。验证安装安装完成后运行以下命令如果看到详细的帮助信息说明安装成功。dev-browser --help3.2 基础使用你的第一个自动化脚本让我们从一个最简单的例子开始感受一下dev-browser的工作流程。我们的任务是打开百度首页获取其标题。步骤1编写脚本创建一个简单的JavaScript脚本内容如下。这个脚本的逻辑是获取或创建一个名为“test”的页面然后导航到百度最后打印页面标题。const page await browser.getPage(test); await page.goto(https://www.baidu.com, { waitUntil: domcontentloaded }); const title await page.title(); console.log(页面标题是:, title);步骤2通过CLI执行在终端中你可以使用“heredoc”语法或者管道来传递脚本。方法A推荐适用于多行脚本dev-browser --headless EOF const page await browser.getPage(test); await page.goto(https://www.baidu.com, { waitUntil: domcontentloaded }); const title await page.title(); console.log(页面标题是:, title); EOF方法B单行或简单脚本echo const pageawait browser.getPage(test);await page.goto(https://www.baidu.com);console.log(await page.title()); | dev-browser --headless步骤3查看结果执行后你会在终端看到类似这样的输出页面标题是: 百度一下你就知道同时因为使用了--headless参数浏览器会在后台无界面运行任务完成后自动关闭。实操心得{ waitUntil: “domcontentloaded” }这个选项非常有用。默认情况下page.goto()会等待页面load事件触发这对于许多单页应用SPA或加载了大量资源的页面来说可能太慢了。domcontentloaded只等待HTML文档被完全加载和解析不等待样式表、图片和子框架通常能更快地进入交互状态。根据目标网站的特点灵活选择waitUntil策略能显著提升脚本执行速度。3.3 进阶操作连接现有浏览器与复杂交互连接到你正在使用的Chrome这是dev-browser最强大的功能之一。首先你需要以调试模式启动ChromemacOS/Linux:在终端运行/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port9222路径可能不同。Windows:在PowerShell或运行对话框中使用完整路径例如“C:\Program Files\Google\Chrome\Application\chrome.exe” --remote-debugging-port9222。更简单的方法直接关闭所有Chrome实例然后用上述命令启动一个新的。或者如果你能找到Chrome的快捷方式在其“属性”-“目标”字段末尾添加--remote-debugging-port9222。启动后打开chrome://inspect/#devices你应该能看到 “Remote Target” 下面列出你打开的标签页。现在让dev-browser连接上去并列出所有标签页dev-browser --connect EOF const tabs await browser.listPages(); console.log(JSON.stringify(tabs, null, 2)); // 漂亮地打印JSON EOF你会得到一个包含所有标签页ID、URL和标题的数组。你可以用browser.getPage(targetId)来获取对特定标签页的控制权。执行一个复杂的交互任务模拟登录假设我们要自动化登录一个假想的测试网站https://example.test/login。const page await browser.getPage(“loginTask”); // 1. 导航到登录页 await page.goto(“https://example.test/login”, { waitUntil: “networkidle” }); // 等待网络空闲 // 2. 填写表单 // 使用更可靠的定位方式根据label文本找到输入框 await page.locator(‘input[name“username”]’).fill(“my_username”); await page.locator(‘input[type“password”]’).fill(“my_password”); // 3. 处理可能的验证码或额外步骤这里假设有个复选框 await page.locator(‘#agree-terms’).check(); // 4. 点击登录按钮并等待导航完成 await Promise.all([ page.waitForNavigation({ waitUntil: ‘networkidle’ }), // 等待导航发生 page.locator(‘button:has-text(“Sign In”)’).click() // 点击按钮 ]); // 5. 验证登录成功 const successElement page.locator(‘.welcome-message’); if (await successElement.isVisible()) { const welcomeText await successElement.textContent(); console.log(‘登录成功’, welcomeText); // 可以保存登录后的Cookie或状态供后续脚本使用通过命名页面持久化 } else { console.error(‘登录可能失败未找到欢迎信息。’); // 可以在这里截图方便调试 const screenshotPath await saveScreenshot(await page.screenshot({ fullPage: true }), ‘login-failure.png’); console.log(‘截图已保存至:’, screenshotPath); }这个脚本展示了几个关键点使用locatorAPIPlaywright的locator比简单的page.click(‘selector’)更强大、更抗变。它支持文本匹配has-text、层级关系等。等待策略waitForNavigation与click的Promise.all组合是处理点击后页面跳转的标准模式。错误处理与调试通过isVisible()判断元素状态失败时截图保存到沙箱允许的临时目录路径通过saveScreenshot函数返回。3.4 与Claude Code深度集成实现无缝自动化dev-browser作为Claude Skill使用时体验最为流畅。你需要做的不是安装一个“插件”而是简单地配置权限。配置Claude Code的权限Claude Code出于安全考虑默认会询问是否允许运行任何bash命令。为了让它能直接运行dev-browser你需要将其加入“允许列表”。找到你的Claude Code设置目录。通常是项目根目录下的.claude/settings.json或者用户主目录下的~/.claude/settings.json。编辑或创建settings.json文件添加以下内容{ “permissions”: { “allow”: [ “Bash(dev-browser *)” ] } }Bash(dev-browser *)这个模式匹配任何以dev-browser开头的命令后面可以跟任何参数如--headless,--connect。在Claude Code中直接使用配置好后你可以在Claude Code的聊天框中直接这样与AI协作你“帮我去GitHub上看看SawyerHood/dev-browser这个项目最新的issue标题是什么。”Claude Code“好的我来写一个脚本用dev-browser获取这个信息。”dev-browser --headless EOF const page await browser.getPage(“github”); await page.goto(“https://github.com/SawyerHood/dev-browser/issues”); // 等待议题列表加载 await page.waitForSelector(‘[data-testid“issue-list”]’); // 获取第一个议题的标题 const firstIssueTitle await page.locator(‘[data-testid“issue-list”] a[data-hovercard-type“issue”]’).first().textContent(); console.log(‘最新的议题标题是:’, firstIssueTitle.trim()); EOFClaude Code执行上述命令后“最新的议题标题是: ‘Feature request: Support for Firefox browser’。”整个过程AI负责理解你的意图、生成精准的Playwright脚本并通过dev-browser安全地执行。你无需离开编辑环境也无需手动运行任何命令。注意事项在Claude Code中如果AI生成的脚本需要多行输入使用 ‘EOF’ … EOF的heredoc语法是最清晰的方式。确保结束的EOF独占一行且顶格。另外虽然dev-browser很安全但允许AI运行bash命令本身有一定风险。建议只在可信的项目或环境中进行全局配置或者仅在你监督下使用。4. 脚本API深度解析与最佳实践4.1 核心API详解dev-browser的脚本环境提供了精心设计的一组全局API。browser对象控制浏览器的核心browser.getPage(nameOrId): 这是最常用的方法。如果传入一个字符串如“main”它会查找或创建一个具有该名称的持久化页面。如果传入一个从browser.listPages()获取的targetId则会连接到对应的特定标签页。返回一个完整的Playwright Page对象。browser.newPage(): 创建一个新的匿名页面。该页面不会持久化当脚本执行完毕后会被自动清理。适合一次性的、独立的浏览任务。browser.listPages(): 返回一个数组包含所有可连接的浏览器标签页信息。每个对象包含id(targetId),url,title,name(如果有的话)。在连接模式下你可以看到你手动打开的所有标签页。browser.closePage(name): 关闭一个通过名称管理的持久化页面。受限的文件IOawait saveScreenshot(buffer, filename): 接受一个截图Buffer来自page.screenshot()和一个文件名将图片保存到沙箱的临时目录~/.dev-browser/tmp/并返回保存的完整路径。这个路径你可以通过console.log输出供用户查看。await writeFile(filename, data): 将文本或二进制data写入临时目录的文件。await readFile(filename): 从临时目录读取文件内容。重要限制所有文件操作都被严格限定在~/.dev-browser/tmp/目录下。脚本无法通过../../../这样的路径遍历到系统其他位置。这是沙箱安全的关键一环。console对象标准的console.log,.warn,.error,.info方法在脚本中可用输出会重定向到运行dev-browser命令的终端标准输出和标准错误流。这是脚本与外界通信的主要方式。4.2 高效定位元素Playwright Locator的精髓AI生成的脚本是否健壮很大程度上取决于元素定位策略。dev-browser支持全部Playwright Locator API以下是一些最佳实践优先使用语义化属性page.locator(‘[data-testid“submit-button”]’)或page.locator(‘button[type“submit”]’)比page.locator(‘#root div button:nth-child(3)’)要稳定得多因为后者极易因DOM结构微小变动而失效。善用文本匹配page.locator(‘button:has-text(“登录”)’)或page.getByText(“登录”)非常直观但要注意文本可能被翻译或改变。可以结合其他属性使用。使用getByRole这是最推荐的方式之一因为它基于ARIA角色可访问性最好也最稳定。例如page.getByRole(‘button’, { name: ‘Submit’ })。等待元素状态在操作元素前先等待它处于可用状态。const button page.locator(‘button.submit’); await button.waitFor({ state: ‘visible’ }); await button.waitFor({ state: ‘enabled’ }); // 确保不是禁用状态 await button.click();处理动态内容与Shadow DOM对于现代前端框架生成的动态内容page.locator()本身会自适应。对于Shadow DOM可以使用locator(‘…’).shadowRoot.locator(‘…’)的链式调用或者Playwright的elementHandle相关方法在沙箱中需注意可用性。4.3 错误处理与脚本健壮性在自动化脚本中错误处理不是可选项而是必选项。因为网络、页面加载时间、元素微小的渲染差异都可能导致脚本失败。基本的Try-Catchtry { await page.goto(‘https://unstable-site.example.com’, { timeout: 10000 }); // 设置超时 // … 其他操作 } catch (error) { console.error(‘导航或操作失败:’, error.message); // 尝试恢复或记录状态 const screenshotPath await saveScreenshot(await page.screenshot(), ‘error-state.png’); console.log(‘错误截图保存在:’, screenshotPath); // 可以选择退出或抛出错误让外部知道任务失败 throw error; }设置合理的超时Playwright的许多方法都接受timeout选项。为网络请求和元素等待设置一个合理的超时如10-30秒避免脚本因个别请求挂起而无限期卡住。await page.click(‘button’, { timeout: 15000 }); // 点击操作最多等15秒 await page.waitForSelector(‘.success-message’, { timeout: 10000, state: ‘visible’ });验证操作结果不要假设操作一定成功。点击按钮后验证页面是否发生了预期的变化。await page.locator(‘#delete-btn’).click(); // 等待确认弹窗出现或者列表项消失 try { await page.locator(‘.confirm-dialog’).waitFor({ state: ‘visible’, timeout: 5000 }); console.log(‘删除操作需要确认。’); } catch { // 可能没有确认对话框直接删除了 await page.locator(‘.item’).first().waitFor({ state: ‘detached’, timeout: 5000 }); console.log(‘项目已删除。’); }5. 常见问题排查与性能优化5.1 连接问题与调试技巧问题运行dev-browser --connect时报错提示无法连接到浏览器。检查Chrome是否以调试模式启动确保你启动Chrome时包含了--remote-debugging-port9222参数。可以打开chrome://inspect/#devices来确认“Remote Target”列表是否为空。检查端口占用默认端口9222可能被其他进程占用。可以尝试换一个端口如--remote-debugging-port9223并在连接时指定dev-browser --connect --port 9223。Windows防火墙在Windows上首次连接时防火墙可能会弹出警告需要允许连接。使用--verbose标志运行dev-browser --connect --verbose可以输出更详细的连接日志帮助定位问题。问题脚本执行时报Target closed或Protocol error。页面被关闭你可能手动关闭了浏览器标签页或者浏览器进程意外退出。确保目标页面在整个脚本执行期间保持打开。浏览器崩溃尝试减少并发操作或降低任务复杂度。对于资源消耗大的页面可以尝试增加headless: false模式观察但dev-browser主要面向无头模式。会话过期对于需要登录的网站长时间运行的脚本可能会遇到会话过期。需要在脚本中加入检测和重新登录的逻辑。5.2 性能优化建议重用浏览器实例和页面这是最重要的优化。避免在每个脚本中都使用--headless启动新浏览器。对于一系列相关任务使用--connect连接到一个长期运行的浏览器并通过browser.getPage(“work”)重用同一个页面对象。浏览器的启动开销是巨大的。选择合适的等待策略domcontentloaded: 最快适合静态页面或你只需要DOM结构。load: 默认等待所有资源加载。networkidle: 等待网络活动停止通常没有超过500ms的网络请求适合单页应用SPA。但超时时间可能较长可通过timeout参数控制。避免不必要的截图和全页抓取page.screenshot({ fullPage: true })和获取整个page.content()非常耗时且数据量大。尽量只截取需要的区域或使用page.snapshotForAI()获取AI优化的轻量快照。并行化独立任务如果脚本有多个独立的任务例如从几个不同的、不相关的标签页抓取数据可以考虑使用Promise.all()来并行执行但要注意沙箱环境对并发的支持程度。清理资源对于使用browser.newPage()创建的临时页面脚本结束后会自动关闭。但对于命名页面如果确定不再需要可以显式调用browser.closePage(name)来释放资源。5.3 安全边界再确认尽管dev-browser设计了沙箱但作为用户你仍需明确其安全边界浏览器本身是真实的脚本通过Playwright控制的是一个真实的Chromium/Chrome浏览器。这意味着如果脚本让浏览器导航到一个恶意网站该网站仍然可能通过浏览器漏洞概率极低进行攻击。因此不要用高权限的系统账户运行dev-browser。浏览器可以访问你登录的状态在--connect模式下脚本可以操作你已经登录了各种账户如Gmail、网银的浏览器标签页。切勿在不受信任的AI生成脚本上使用此模式。最好为自动化任务创建独立的浏览器配置文件。临时目录是唯一的出口脚本只能读写~/.dev-browser/tmp/。定期清理这个目录是个好习惯。也要注意如果脚本将敏感信息如抓取的数据写入文件这些信息会留在这个目录中。网络访问通过浏览器脚本本身不能直接发起网络请求但它控制的浏览器可以。这意味着脚本可以指示浏览器访问任何网站。结合第一条需保持警惕。我个人在实际使用dev-browser几个月后最大的体会是它极大地模糊了“AI构思”和“AI执行”的界限。以前需要“AI生成代码 - 我复制到本地 - 我安装依赖 - 我运行调试 - 我返回结果给AI”的漫长循环现在变成了“AI生成代码并直接看到结果”的即时反馈。这不仅仅是效率的提升更是一种工作流的质变。它让AI从单纯的“代码建议者”变成了真正的“任务执行伙伴”。当然强大的能力也意味着更大的责任时刻牢记安全边界在可信的环境下用它来解放生产力才是正确的打开方式。