前言¶
让 AI 编程助手改一版前端、再帮你「点一遍页面看对不对」,听起来合理,实际却常卡在流程上:每次验证都重新拉起浏览器、重新找页面句柄;改完代码后是该 reload 还是整进程重启说不清;功能路径走通了,却没单独做视觉检查,首屏裁切、对比度、动效中途状态这类问题容易漏掉。和「一次性 CLI 跑通某个网页流程」相比,本地 Web 或 Electron 应用的调试更需要同一会话里反复迭代。
OpenAI 在精选技能库中提供了 playwright-interactive Skill。它不走终端里的一次性 playwright-cli 命令流,而是通过持久化的 js_repl 会话挂住 Playwright / Electron 句柄,让 Agent 像开发者一样:改代码 → 刷新或重启 → 功能 QA → 视觉 QA → 截图留证。本文依据官方 SKILL.md、同仓库的 playwright Skill,以及 Codex 相关 Issue 交叉核实后整理。
这是什么¶
playwright-interactive 是一个 Agent Skill(通用 SKILL.md 格式),收录于 OpenAI 维护的 openai/skills 仓库 skills/.curated/playwright-interactive 目录。官方一句话定位是:
通过
js_repl做持久化的浏览器与 Electron 交互,用于快速迭代的 UI 调试。
它解决的问题很具体:在本地调试 Web 或 Electron 应用时,跨多轮修改保持同一套 Playwright 句柄(browser / context / page,或 electronApp / appWindow),在不必每次重启整套工具链的前提下,完成功能验证与视觉 QA。
同仓库还有面向 CLI 的 playwright Skill(playwright-cli:打开页面、snapshot、按 ref 点击)。二者互补:playwright 更适合「对某个 URL 跑一遍自动化」;playwright-interactive 更适合「对着正在开发的本地应用,像 REPL 一样边改边验」。
需要事先说明:openai/skills 仓库 README 已标注 deprecated,后续示例转向 OpenAI Plugins。更关键的是,该 Skill 强依赖的 js_repl 在当前 Codex 中已被标记为 removed(社区 Issue 与 codex features list 输出可交叉确认)。因此下文按官方 SKILL.md 如实介绍其设计与用法,并在注意事项中单独写清运行时前提——不要把它当作在所有当前 Codex 环境中都能开箱即用的能力。
核心功能与亮点¶
核实官方资料后,能力可以概括为以下几点。
1. 持久化 Playwright 会话,而不是每次冷启动
在 js_repl 里用 var 声明顶层句柄(browser、context、page、electronApp、appWindow 等),后续 cell 直接复用。官方明确:把 js_repl_reset 当作恢复手段,不要当日常清理——重置内核会毁掉所有 Playwright 句柄。
2. 覆盖桌面 Web、移动 Web、原生窗口与 Electron
- 桌面 Web:默认显式 viewport(如
1600×900),便于可复现截图与断点检查。 - 移动 Web:单独的
mobileContext/mobilePage(如390×844,isMobile+hasTouch)。 - 原生窗口模式:
viewport: null,用于验证真实窗口尺寸、系统 DPI、浏览器 chrome 相关行为。 - Electron:通过 Playwright 的
_electron.launch,按真实桌面窗口处理(noDefaultViewport)。
官方建议:例行迭代用显式 viewport;需要最终环境相关签收时,再单独做一轮 native-window;模式切换视为 context 重置,不要混用。
3. 先建 QA 清单,再分功能 QA 与视觉 QA
测试前要写一份共享 QA inventory,来源包括:用户需求、已实现的用户可见行为、最终回复里打算签收的声明。功能 QA 要求用真实用户输入(键盘、鼠标、点击、触摸等 Playwright API),page.evaluate / electronApp.evaluate 可以探查状态,但不能算签收输入。视觉 QA 与功能 QA 分开,且用户可见声明必须在「该声明应被感知的具体状态」下检查。
4. 改代码后的 reload / relaunch 决策清晰
- 仅渲染层改动:Web 用
reloadWebContexts(),Electron 用appWindow.reload(...)。 - 主进程、preload 或启动逻辑改动:关闭并重新
launchElectron。 - 对进程归属或启动代码不确定时:宁可 relaunch,不要猜测。
5. 截图与模型坐标对齐、视口适配检查内建
若通过 codex.emitImage(...) 把截图交给模型解读,官方默认要求归一化到 CSS 像素,避免 Retina / 高 DPI 下设备像素坐标与 Playwright CSS 坐标错位。签收前还要做 viewport fit:截图为主、数值检查为辅;出现裁切、遮挡、被挤出视口,即使 scroll 指标看起来正常,也视为失败。
安装与启用¶
Agent Skill 的通用形态是:一个目录 + SKILL.md(本 Skill 另含 agents/、assets/ 等可选资源)。各工具扫描路径不同,以下为可核实的安装方式。
在 Codex 中安装 Skill 本体¶
curated 技能可用 Codex 内的 $skill-installer 按名称安装:
$skill-installer playwright-interactive
或指定 GitHub 目录:
$skill-installer install https://github.com/openai/skills/tree/main/skills/.curated/playwright-interactive
安装后通常位于 $CODEX_HOME/skills(默认 ~/.codex/skills)。若未自动出现,需重启 Codex。
启用 js_repl(Skill 前置条件)¶
官方 SKILL.md 要求本 Skill 必须启用 js_repl。若缺失,在 ~/.codex/config.toml 中配置:
[features]
js_repl = true
也可启动新会话时加:
codex --enable js_repl
# 等价于 -c features.js_repl=true
启用后需新开 Codex 会话刷新工具列表。官方还写明:在 js_repl + Playwright 沙箱支持完成前,需暂时关闭沙箱,例如:
codex --sandbox danger-full-access
或将 sandbox_mode 配成 danger-full-access。
交叉核实:在较新的 Codex CLI(社区报告如
0.128.0)中,codex features list可能显示js_repl/js_repl_tools_only为removed。此时按文档启用也不会出现对应工具,Skill「按原文」无法工作。社区讨论中更接近的替代运行时是node_repl(及 Browser Use 文档中的相关 MCP 表面),但官方 curated 的本 Skill 正文仍写的是js_repl,迁移 PR 是否合入需以仓库现状为准。
在 Cursor / Claude Code 等工具中¶
通用 Agent Skills 标准下,可将该目录放到工具约定路径,例如:
- Cursor:项目级
.cursor/skills/playwright-interactive/,或用户级~/.cursor/skills/;亦兼容.agents/skills/、.codex/skills/等。 - Claude Code:
.claude/skills/playwright-interactive/或~/.claude/skills/。
注意:Skill 指令深度绑定 Codex 的 js_repl、codex.emitImage 等运行时能力。把 SKILL.md 拷到 Cursor / Claude Code 后,Agent 仍可能缺少同等持久化 JS REPL,只能借鉴其中的 QA 清单、reload 决策与截图规范,不能假设「复制目录即可完整复现」。
一次性项目依赖安装¶
在要调试的项目目录执行(换工作区需重做):
test -f package.json || npm init -y
npm install playwright
# 仅 Web、需要 headed Chromium 或移动模拟时:
# npx playwright install chromium
# 仅 Electron,且当前工作区就是应用本身时:
# npm install --save-dev electron
node -e "import('playwright').then(() => console.log('playwright import ok')).catch((error) => { console.error(error); process.exit(1); })"
本地 Web 调试时,用持久 TTY 会话跑开发服务器(如 npm start),不要依赖短命后台命令;page.goto 前确认端口已在监听。
典型用法示例¶
以下片段均来自官方 SKILL.md,可按 cell 在 js_repl 中执行。
1. Bootstrap(只跑一次)¶
var chromium;
var electronLauncher;
var browser;
var context;
var page;
var mobileContext;
var mobilePage;
var electronApp;
var appWindow;
try {
({ chromium, _electron: electronLauncher } = await import("playwright"));
console.log("Playwright loaded");
} catch (error) {
throw new Error(
`Could not load playwright from the current js_repl cwd. Run the setup commands from this workspace first. Original error: ${error}`
);
}
绑定规则:共享句柄用 var,以便后续 cell 复用;句柄看起来过期时,将该绑定设为 undefined 后重跑对应 cell,而不是到处加恢复逻辑。
2. 启动或复用桌面 Web 会话¶
本地地址优先用 127.0.0.1,少用 localhost:
var TARGET_URL = "http://127.0.0.1:3000";
if (page?.isClosed()) page = undefined;
await ensureWebBrowser();
context ??= await browser.newContext({
viewport: { width: 1600, height: 900 },
});
page ??= await context.newPage();
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded:", await page.title());
其中 ensureWebBrowser 等 helper 在官方 Bootstrap 后的 Shared web helpers 一节给出:浏览器断开则清空并重 launch(默认 headless: false)。
3. 迭代中的刷新与 Electron 重启¶
Web 渲染层刷新:
await reloadWebContexts();
Electron 仅渲染层:
await appWindow.reload({ waitUntil: "domcontentloaded" });
console.log("Reloaded Electron window");
主进程 / preload / 启动相关改动后重启 Electron:
await electronApp.close().catch(() => {});
electronApp = undefined;
appWindow = undefined;
electronApp = await electronLauncher.launch({
args: [ELECTRON_ENTRY],
});
appWindow = await electronApp.firstWindow();
console.log("Relaunched Electron window:", await appWindow.title());
4. 任务真正结束时再 Cleanup¶
退出 Codex、关终端或丢失 js_repl 不会隐式执行 browser.close() / electronApp.close()。Electron 尤其可能残留进程。官方 cleanup 示例(节选逻辑):
if (electronApp) {
await electronApp.close().catch(() => {});
}
if (mobileContext) {
await mobileContext.close().catch(() => {});
}
if (context) {
await context.close().catch(() => {});
}
if (browser) {
await browser.close().catch(() => {});
}
browser = context = page = undefined;
mobileContext = mobilePage = undefined;
electronApp = appWindow = undefined;
console.log("Playwright session closed");
若马上退出 Codex,应先跑 cleanup,看到 "Playwright session closed" 再退出。
5. 在对话里怎么用¶
可以明确要求 Agent 按该 Skill 工作,例如:
使用 playwright-interactive:对本地 http://127.0.0.1:3000 做一轮 UI 调试。
先写 QA inventory,bootstrap js_repl,保持同一 page 句柄;
我改完代码后只 reload,分别做功能 QA 与视觉 QA,并做 viewport fit 检查。
把目标 URL、是 Web 还是 Electron、是否需要移动端/原生窗口签收写清楚,Agent 才应按官方 core workflow 执行,而不是退回到一次性 CLI 脚本。
适用场景与注意事项¶
适合谁、什么场景:
- 本地 Web 或 Electron 应用的多轮 UI 迭代调试;
- 需要 Agent 在同一浏览器/窗口会话里连续操作、截图、对照声明做签收;
- 既要端到端功能路径,又要单独的视觉 QA、首屏/最小视口适配检查;
- 与
playwright(CLI)Skill 搭配:探索外网或一次性流程用 CLI,贴着本地工程反复改 UI 用 interactive。
限制与坑(官方 + 交叉核实):
- 运行时依赖
js_repl:当前 Codex 若已移除该特性,按SKILL.md原文无法启用;安装 Skill 目录不等于能力可用。 - 临时要求关闭沙箱:官方写明需
--sandbox danger-full-access,有安全与权限含义,勿在不信任的仓库上盲目开启。 js_repl_reset会毁掉句柄:只在内核真正卡住时用。- 常见失败:找不到
playwright模块(未在当前 cwd 安装);缺 Chromium 可执行文件(需npx playwright install chromium);net::ERR_CONNECTION_REFUSED(dev server 未在持久会话中运行);Electron 下不要用context().newPage()当 scratch page(官方明确不支持该路径)。 - 仓库状态:
openai/skills已 deprecated;长期应关注 OpenAI Plugins 与 Codex Skills 文档 的替代方案。
小结¶
playwright-interactive 把「持久化浏览器/Electron 句柄 + 共享 QA 清单 + 功能/视觉分轨签收 + reload/relaunch 决策」固化成 Agent 可执行的工作流,目标是让 UI 调试像开发者坐在 headed 浏览器前一样可迭代,而不是每次从冷启动脚本重来。它与 CLI 向的 playwright Skill 形成互补,但强依赖 js_repl 等运行时;在当前 Codex 中启用前,务必先确认本机是否仍暴露该工具。
官方目录:https://github.com/openai/skills/tree/main/skills/.curated/playwright-interactive
相关对照:https://github.com/openai/skills/tree/main/skills/.curated/playwright