Route Runtime.enable/disable through the incoming child sessionId instead of always using the tab root session so OOPIF iframe targets receive execution contexts reliably. Add a focused relay regression test for empty-src cross-origin iframe attach flow and update extension version/changelog for release tracking.
Retry the post-goto readyState check when Chromium tears down the execution context during ongoing navigation, so the relay navigation test validates page readiness without intermittent race failures.
getStableRefFromAttributes checked id before data-testid, opposite of
what getRefsForLocators does. Now both are consistent: test IDs first,
id second. Test IDs are developer-intentional and stable, id is often
auto-generated by frameworks.
Remove the custom WebSocket-based CDPSession class entirely. All CDP
communication now goes through PlaywrightCDPSessionAdapter which wraps
Playwright's existing CDP session, avoiding duplicate WebSocket connections
and Target.attachToTarget calls that the relay intercepts.
ICDPSession interface uses ProtocolMapping generics from devtools-protocol
so send/on/off methods are fully type-safe — return types are inferred
from the CDP command string (e.g. 'Page.getLayoutMetrics' returns
Protocol.Page.GetLayoutMetricsResponse).
- Remove CDPSession class and getCDPSessionForPage WebSocket factory
- Remove wsUrl parameter from all snapshot/label functions
- Remove CDPSession cache from executor (adapter is stateless)
- Remove 'as CDPSession' casts from debugger, editor, styles, react-source
- Rename getExistingCDPSessionForPage → getCDPSessionForPage
- Remove test for old getCDPSession through relay
Switch from getCDPSessionForPage (creates a new WS via Target.attachToTarget)
to getExistingCDPSessionForPage which reuses Playwright's internal CRSession.
This is critical for the relay server where Target.attachToTarget is intercepted
and cannot create real new sessions.
Also add FrameLocator support in accessibilitySnapshot — FrameLocator (from
locator.contentFrame()) is now auto-resolved to the real Frame object needed
for OOPIF CDP session attachment.
- New PlaywrightCDPSessionAdapter wrapping Playwright's CDPSession as ICDPSession
- New getExistingCDPSessionForPage() using context.getExistingCDPSession()
- resolveFrame() helper converts FrameLocator to Frame via elementHandle().contentFrame()
- getAriaSnapshot() frame param now accepts Frame | FrameLocator
- Executor CDP cache uses ICDPSession, borrowed sessions detach as no-op
- Test for getExistingCDPSession through the relay
- Updated framer iframe guide with alternative page.frames() example
- Bump playwright submodule to @xmorse/playwright-core@1.59.2
- Fix styles-api.md import path
Chrome deprecated Debugger.setScriptSource in Chrome 142+ (removed in 145).
Falls back to Runtime.evaluate to re-execute modified scripts, which
works for scripts defining functions at global scope.
- Add "No changes since last call" hints for snapshots/html/markdown
- Adjust truncation message to suggest search
- Update skill.md snapshot behavior doc
- Resolve extension connections by stable key and map to current connection
- Reject Playwright clients without explicit extension binding when multiple browsers connected
- Reject duplicate clientId connections
- Allow default extension only when a single browser is connected
- Include stableKey in /extensions/status response
- Move recording cleanup before connection delete (was no-op)
- Only fallback to default extension when extensionId is null
- Add cwd param to /cli/session/new, CLI passes process.cwd()
- Require pre-existing sessions for execute/reset endpoints
- Remove duplicate ensureRelayServer call in CLI
- Increase fetch timeouts from 500ms to 2000ms
- Extension connections are now tracked per-connection with isolated state
- Each connection has its own connectedTargets, pendingRequests, messageId
- Sessions are bound to specific extensionId via SessionMetadata
- New /extensions/status endpoint for listing all connected browsers
- CLI session new shows browser selection when multiple connected
- Browser detection via navigator.userAgentData (Ghost, Brave, Edge, etc)
- Added identity permission for Chrome profile email detection
- Constants, types, proxy factory, and handler in one file
- Works in both Node.js (executor) and Chrome (extension) environments
- executor.ts imports createGhostBrowserChrome factory
- background.ts imports handleGhostBrowserCommand handler
- Parse code with acorn to detect single expression statements
- Auto-wrap expressions with 'return await (...)' for implicit return
- Exclude side-effects: assignments, updates (x++), delete operator
- Update skill.md examples to remove console.log (keep await)
- Add unit tests for shouldAutoReturn function
The test was slow due to:
1. Page scanning by content (slow waitForLoadState + content() per page)
2. Using example.com which only has inline scripts with empty URLs
Fix:
- Use URL matching instead of content scanning (fast)
- Use news.ycombinator.com which has scripts with actual URLs
- The Debugger class filters out scripts without URLs, so we need
a page that actually has external script files
Total relay-session.test.ts time: 74s → 27s
Two tests were taking 30s and 10s respectively due to avoidable waits:
1. 'should pause on all exceptions with setPauseOnExceptions' (30.6s → 0.5s)
- Was awaiting page.evaluate() while debugger was paused
- Playwright's evaluate has 30s timeout, so it sat there waiting
- Fix: use Runtime.evaluate via CDP session (don't await until after resume)
2. 'should manage breakpoints with Debugger class' (10.6s → 0.4s)
- Was connecting over CDP and scanning all pages with waitForLoadState
- Each page waited up to 5s for domcontentloaded + content check
- Fix: use getCDPSessionForPage directly on the page we just created
- no-change returns newContent directly
- full returns newContent directly (no 'Content changed significantly' prefix)
- diff only returned when shorter than full content
- First call returns full content (no previous snapshot stored)
- No-change case returns full content instead of message
- Diff only returned when shorter than full content
- Fixed test to use showDiffSinceLastCall: false for search