Playwright can test Chrome extensions. So why does my AI agent still need my help?

작성자

카테고리:

← 피드로
DEV Community · frog404 · 2026-07-20 개발(SW)

What happens when you let an AI write a browser extension

I’ve been building Chrome extensions with AI coding agents — Claude Code, Cursor, whatever. Writing the code is the fast part. The problem is everything after.

“Build it, load it into the browser, open the popup, click the button, check the console.”

That part? All me. Every time. The agent can write the code, but it can’t see what the code looks like on screen. It can’t click anything. It can’t read the logs. So the human becomes the agent’s eyes and hands.

At first I accepted this as the cost of doing business. Then I hit the UI-polishing phase and it turned into hell. Every “move that button 20px right” or “make the font 14px” meant: build → load → screenshot → paste it into chat → “how’s this?” → agent edits code → build → load → screenshot. Forever.

And this isn’t just a Chrome extension problem. Electron apps, VSCode extensions — anything with a UI that doesn’t open in a normal browser tab has the same failure mode.

“Just use Playwright” — okay, let’s talk about that

For a normal web app, sure. Playwright opens the page, screenshots it, clicks things. Tell your agent “verify it with Playwright” and it does.

Playwright supports Chrome extensions too — there are official docs, and they work. But the setup is nothing like a regular page. You need launchPersistentContext to load the extension, extract the extension ID dynamically from the Service Worker URL, then navigate to chrome-extension://{id}/popup.html. Popups have a different lifecycle from normal pages, and MV3 Service Workers get suspended when idle — Playwright handles most restarts transparently these days, but it’s still one more extension-specific concern the agent has to account for.

As test code written by a human, this is all fine. Follow the docs and it works.

The problem is asking an agent to do it. All I want to say is “build and check it.” Instead, the agent has to write Playwright setup code, understand extension-specific context management, write a test, run it, and parse the output. That’s a lot of machinery for “does the button look right?”

Electron is similar. Playwright has (experimental) Electron support — electron.launch gets you basic UI automation. But native OS dialogs like dialog.showOpenDialog() live outside the DOM, so you need to inject mocks. Want to observe IPC between main and renderer? Build your own instrumentation. And Electron bugs love to hide on the far side of IPC, so seeing only the window is half a verification.

VSCode extensions have a different official testing path entirely. The standard setup uses @vscode/test-electron to launch an Extension Development Host — VSCode itself is an Electron app, so you can drive the window through Playwright’s Electron support, but commands, notifications, output channels, and the Problems panel all need VSCode-specific integration on top.

So: every platform has good primitives, and agent-facing tools like Playwright MCP already cover driving regular browser pages. What I couldn’t find was a single agent-facing interface that covered Chrome extension lifecycle management (build, reload, Service Workers, content scripts), Electron IPC, and VSCode-specific operations under one operation model.

The primitives already existed. What was missing was a unified development loop an agent could call without rebuilding the platform-specific setup every time.

So I built it.

KamoX: hide the platform mess behind a local HTTP API

KamoX is a local HTTP API server. It’s not a Playwright replacement — think of it as a layer that wraps Playwright and each platform’s native tooling behind endpoints an agent can hit with curl.

  • POST /rebuild — build and reload
  • POST /check-ui — screenshot + DOM info + console errors, in one call
  • POST /playwright/element — click buttons, fill forms
  • GET /logs — retrieve logs

/check-ui returns the screenshot path plus the page title, body text, HTML, console logs, and page errors as text. So even an agent that can’t look at images can reason about UI state from the text:

{
  "success": true,
  "data": {
    "loaded": true,
    "screenshot": "/project/.kamox/screenshots/popup_1234567890.png",
    "dom": { "title": "Popup Title", "bodyText": "...", "html": "<html>...</html>" },
    "logs": [],
    "errors": [],
    "performance": { "loadTime": 240 }
  }
}

Enter fullscreen mode Exit fullscreen mode

A typical agent loop looks like this:

# after editing code, rebuild
curl -X POST http://localhost:3000/rebuild

# check the UI (keepOpen leaves the popup up)
curl -X POST http://localhost:3000/check-ui \
  -H "Content-Type: application/json" \
  -d '{"keepOpen": true}'

# click a button in the open popup
curl -X POST http://localhost:3000/playwright/element \
  -H "Content-Type: application/json" \
  -d '{"pageType": "popup", "selector": "#save-btn", "action": "click"}'

# read the logs
curl http://localhost:3000/logs

Enter fullscreen mode Exit fullscreen mode

Why HTTP instead of MCP?

A deliberate choice. I picked HTTP as the core interface because it’s easy to inspect, easy to script, and callable from any agent that can run shell commands and reach localhost — plus the same API works from plain scripts and CI, not just agents. Requests and responses are trivially debuggable by a human with curl.

An MCP adapter could be layered on top later, but the underlying platform API doesn’t need to depend on any particular client protocol. For a local dev server, plain HTTP is the simplest possible integration surface.

Why a plugin architecture?

Chrome extensions, Electron apps, and VSCode extensions launch differently and debug differently. But what the agent wants is identical: build, look at the screen, press a button, read the logs.

So the core owns the common API, and platform-specific dirt lives in plugins: waking idle Service Workers and reading their logs via CDP (Chrome), IPC monitoring and native-dialog mocks (Electron), command execution and Problems-panel access (VSCode).

kamox chrome --auto-build
kamox electron --entryPoint main.js
kamox vscode --project-path .

Enter fullscreen mode Exit fullscreen mode

One command starts the server; the agent takes it from there.

What actually changed

The biggest shift: I can now say “hit localhost:3000 and check your work.”

Before, every CSS tweak meant me building, opening the browser, screenshotting, and pasting. Now the agent runs /rebuild, pulls a screenshot and DOM info from /check-ui, clicks through with /playwright/element if something looks off, grabs errors from /logs, and fixes its own code. Just eliminating the screenshot-and-paste ritual changed how the whole loop feels.

It’s not perfect, and I’d rather be upfront about that:

  • KamoX opens popup.html as a regular page. DOM and visuals are verifiable, but real-toolbar-popup behaviors like focus handling and auto-close aren’t reproduced.
  • “Is this design actually good?” is still a human call.
  • Extension permissions and platform-specific sharp edges still exist.
  • KamoX is a local development tool — don’t expose its HTTP port to untrusted networks.

But I no longer spend hours acting as my agent’s eyes and hands, and on UI-heavy work the difference is obvious.

There’s also kamox guide, which prints an LLM-optimized API reference you can point your CLAUDE.md or .cursorrules at, and scenario files for defining preconditions. Honestly though, 90% of usage is /rebuild + /check-ui + /logs.

Try it

npm install -g kamox
kamox chrome --auto-build

Enter fullscreen mode Exit fullscreen mode

Chrome extension, Electron, and VSCode modes are implemented (VSCode mode is currently tested mainly on Windows). MIT licensed.

GitHub: https://github.com/iwabuchi404/kamox

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다