前言¶
讓 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