Commit Graph

830 Commits

Author SHA1 Message Date
Tommy D. Rossi 5a3acd9cb8 refactor liveline: extract Section, List, Li components
Simplifies article content by extracting repeated patterns into
reusable components:
- Section: wraps Divider + SectionHeading + children
- List: styled ul with typography inline styles
- Li: styled li with correct padding

Article JSX now reads as clean declarative markup instead of
verbose inline-styled HTML.
2026-02-19 10:04:45 +01:00
Tommy D. Rossi a8481af89b add liveline page: recreation of benji.org/liveline editorial design
Recreates the benji.org/liveline page in the website folder using React,
Tailwind, and the same editorial design system. Charts are replaced with
static SVG placeholders.

Typography:
- Inter variable font from rsms.me (same source as next/font)
- font-weight 475 body, 560 headings, font-optical-sizing: auto
- 14px body, 20px line-height, -0.09px letter-spacing
- Near-black text rgb(17,17,17), 40% opacity secondary
- Display P3 accent colors with @supports fallback

Layout:
- 550px narrow content column, fixed TOC sidebar
- Stagger-in entrance animation with cascading delays
- Fixed header fade gradient (multi-stop white-to-transparent)
- Scroll-spy TOC highlighting via IntersectionObserver

Code blocks:
- Prism.js syntax highlighting with GitHub-light color palette
- Line numbers column with 20px right padding
- 12px / 18px font size

Props tables, dividers, captions, code blocks, bullet lists all
styled to match the original computed values.
2026-02-19 09:56:30 +01:00
Tommy D. Rossi 38098c6153 fix stale popup warnings leaking across executions on error path
Oracle review found that popupWarnings was only drained on the success
path. If execute() threw, warnings would leak into the next unrelated
call. Now drain in both success and catch paths.

Also guard indexOf returning -1 when popup isn't in context.pages() yet.
2026-02-18 23:13:23 +01:00
Tommy D. Rossi ea4c66b2f5 use page.on('popup') instead of page.opener() for popup detection
page.opener() produced false positives during CDP reconnection because
existing pages re-enumerated via context.pages().forEach would sometimes
have a non-null opener. page.on('popup') only fires for actual
window.open() and target=_blank interactions, so it's safe to attach
on both new and existing pages without false positives.
2026-02-18 23:11:21 +01:00
Tommy D. Rossi 03a70fb9fe warn agents when popup windows are detected during execution
When window.open() creates a popup, agents have no visibility into it
and can't control it via playwriter. This adds a [WARNING] line to the
executor output telling agents the popup page index, URL, and to repeat
the interaction without popups or navigate to the URL directly.

Detection uses page.opener() on newly created pages (context 'page'
event), not on existing pages during reconnection to avoid false
positives.
2026-02-18 22:58:27 +01:00
Tommy D. Rossi 17fd10b50f skill: fix contradictions, remove duplicates, consolidate advice
Fixes:
- Bug: page.screenshotWithAccessibilityLabels was called as method on page,
  fixed to standalone function call
- Removed duplicate 'debugging playwriter issues' section at end of file
  (identical to the detailed one earlier)
- Softened waitForTimeout rule: short waits (1-2s) now acknowledged as
  acceptable for non-deterministic events like popups/animations
- Fixed download example path from /tmp/ to ./
- Added note that bare 'page' in examples is for brevity, real automation
  should use state.myPage
- Consolidated diff behavior explanation to one place in snapshot section
  (was repeated 3 times with slight inconsistencies)
- Trimmed redundant 'prefer snapshots' advice to reference the dedicated
  'choosing between snapshot methods' section
- Consolidated cmd+click popup advice (was in both mistakes and common
  patterns with duplicate examples)
- Cross-referenced iframe approaches: frameLocator for locator chaining,
  contentFrame for Frame objects needed by snapshot()
- Clarified require is the only option in sandbox (ESM import unavailable)
2026-02-18 22:20:52 +01:00
Tommy D. Rossi 18284efd9f skill: add observe feedback loop and print page URL in every observe step
The observe step now has an inner loop — if the page isn't ready (still
loading, wrong URL, expected content missing), the agent loops back to
observe again instead of acting on stale state. The ASCII diagram shows
the 'not ready' branch looping back to observe.

Every observe step in the example now prints page.url() before the
snapshot so the agent always knows the current URL. This catches
redirects and unexpected navigation between actions.
2026-02-18 18:14:37 +01:00
Tommy D. Rossi 539aeaac91 rename accessibilitySnapshot to snapshot for shorter agent usage
The function is now exposed as `snapshot()` in the executor sandbox scope.
`accessibilitySnapshot` remains as a backward-compatible alias pointing to
the same function, so existing agents and saved commands continue to work.

Updated all code examples across skill.md, README, PLAYWRITER_AGENTS,
docs, tests, and the extension welcome page to use the shorter name.
2026-02-18 17:13:57 +01:00
Tommy D. Rossi b5c14cb68d add test proving viewport screenshot dimensions are correct without clip param
In connectOverCDP mode, Playwright already falls back to querying
window.innerWidth/innerHeight for viewport size (screenshotter.ts
_originalViewportSize). This test confirms the screenshot dimensions
match exactly with just scale:'css' and no manual clip, proving
the clip in screenshotWithAccessibilityLabels is redundant.

The test goes through the full extension CDP path:
Playwright → relay server → extension WS → chrome.debugger
2026-02-18 00:56:43 +01:00
Tommy D. Rossi 7ed17d264f skill: add computer use section mapping Anthropic/Claude extension actions to Playwright equivalents
Add a 'computer use' section to skill.md showing how Playwriter covers all
actions from the Anthropic computer_20250124 tool and the Claude Chrome
extension's custom computer tool, using Playwright APIs instead of
screenshot-based coordinate clicking.

Each subsection shows the declarative locator-based approach first (preferred)
with coordinate-based fallback for canvas/maps/custom widgets.

Also adds docs/claude-extension-tools.json with all 16 tool schemas extracted
from the Claude Chrome extension v1.0.39 (minified source) for reference.
2026-02-18 00:44:44 +01:00
Tommy D. Rossi 05700fa53b release: playwriter@0.0.63
Security hardening for privileged HTTP routes (/cli/*, /recording/*).

- Block cross-origin browser requests via Sec-Fetch-Site header validation (browsers always set this forbidden header; Node.js/curl clients don't send it, so they're unaffected)
- Reject POST requests without Content-Type: application/json to prevent the CORS simple-request bypass (text/plain POST skips CORS preflight entirely)
- Enforce token authentication on /cli/* and /recording/* when --token is set (remote access mode), matching the behavior documented in remote-access.md
- Update remote-access.md to clarify which routes require token auth
- Add security regression tests covering all attack vectors: Sec-Fetch-Site blocking, Content-Type enforcement, token validation, and Node.js client pass-through
2026-02-17 15:54:39 +01:00
Tommy D. Rossi 9008920c95 skill: add interaction feedback loop and reuse about:blank tabs
Add observe → act → observe feedback loop section with ASCII diagram
and Framer command palette example. Agents must take a snapshot after
every action to verify the result before proceeding.

Also change page creation pattern to reuse existing about:blank tabs
instead of always creating new ones, with a note to navigate immediately
in the same execute call to prevent concurrent agents from grabbing the
same tab.
2026-02-17 15:53:25 +01:00
Tommy D. Rossi 33996d511f playwriter skill self improvement: prefer snapshots over screenshots, add debugging patterns
Lessons from real debugging session where ~6 screenshot+image-agent roundtrips
wasted context when a single accessibility snapshot filter would have answered.

Changes:
- **checking page state**: snapshots are the default, screenshots only for visual/CSS
- **common mistakes**: added quote escaping in $'...' (#6), screenshot overuse warning (#7)
- **accessibility snapshots**: added JS filtering pattern for large snapshots
- **debugging web apps**: new section using getLatestLogs, DOM inspection via
  evaluate, and combined snapshot+logs patterns
2026-02-16 20:54:21 +01:00
Tommy D. Rossi 55be6ec840 fix: sanitize Buffer and long string output in CLI/MCP logging
Buffer instances (from screenshots, PDFs, responses) were dumping hundreds of
chars of hex bytes via util.inspect. Long strings nested in objects (base64,
HTML) also wasted context window space.

- Add Buffer.prototype[util.inspect.custom] in all entry points (cli, mcp,
  cdp-relay) so Buffers display as '<Buffer N bytes>' instead of hex dumps
- Add maxStringLength: 1000 to all util.inspect calls so strings inside
  inspected objects get capped (top-level strings are untouched, the existing
  10k char overall truncation handles those)
2026-02-16 20:03:36 +01:00
Tommy D. Rossi 44370833f7 feat: bundle playwriter version in extension, warn when CLI/MCP is outdated
The extension now reports which playwriter version it was built with via a
`?v=` query param on its WebSocket connection to the relay. The relay stores
this and exposes it in `/extension/status` and `/extensions/status` endpoints.

CLI and MCP compare the extension's bundled version against their own VERSION.
If the extension was built with a newer playwriter, a warning is shown:

  Playwriter 0.0.60 is outdated (extension requires 0.0.62).
  Run `npm install -g playwriter@latest` or update the playwriter package.

This is fully backward compatible:
- Old extensions without `?v=` get `playwriterVersion: null`, no warning
- No WS protocol changes (version is in URL query params)
- Warning fires once per executor/session lifetime

Flow:
  vite build reads playwriter/package.json → injects __PLAYWRITER_VERSION__
  → extension sends ?v=X.Y.Z on WS connect → relay stores in ExtensionInfo
  → /extension/status returns playwriterVersion → CLI/executor checks & warns
2026-02-16 19:42:58 +01:00
Morse 387149c649 docs: add Chrome launch commands to skill.md for when browser is closed
When the Playwriter extension can't connect because Chrome isn't running,
agents now have platform-specific commands (macOS, Linux, Windows) to start
Chrome from the CLI. Also documents the --allowlisted-extension-id and
--auto-accept-this-tab-capture flags for enabling automatic tab capture
for screen recording without manual extension clicks.

Closes #48
2026-02-16 14:44:12 +01:00
Tommy D. Rossi ce775c06f6 fix: hide frontmatter in remote-access doc from GitHub rendering 2026-02-16 11:32:12 +01:00
Tommy D. Rossi f61509a91f release: playwriter@0.0.62 2026-02-14 16:32:17 +01:00
Tommy D. Rossi fa68e33a78 feat: support HTTPS URLs in PLAYWRITER_HOST + remote access guide
PLAYWRITER_HOST now accepts full URLs like `https://x-tunnel.traforo.dev`
in addition to plain hostnames. Previously it always constructed
`http://${host}:19988` which broke when using traforo tunnels (HTTPS on
port 443).

Added `parseRelayHost()` utility that detects URL protocols and returns
correct HTTP/WebSocket base URLs. Updated all consumers:
- cli.ts: getServerUrl()
- mcp.ts: getLogServerUrl(), checkRemoteServer()
- executor.ts: checkExtensionStatus()
- utils.ts: getCdpUrl() (ws:// → wss:// for HTTPS hosts)

Plain hostnames still work as before (appends :19988).

Added docs/remote-access.md covering:
- Architecture: playwriter serve + traforo tunnel through Cloudflare
- Host and remote machine setup with env vars
- Use cases: remote Mac mini, user support, multi-machine, VM/devcontainer
- Security model: non-guessable URLs, token auth, localhost-only extension
  endpoint, no open ports, visible automation, instant revocation
2026-02-14 16:31:04 +01:00
Tommy D. Rossi 737133641f release: playwriter@0.0.61
Simplify Unix port killing by replacing shell pipelines with direct lsof -t and process.kill().
2026-02-14 00:08:41 +01:00
Tommy D. Rossi 744a9b913a fix: recover relay startup from EADDRINUSE 2026-02-13 15:42:43 +01:00
Tommy D. Rossi 8ffaa9333b Add GitHub Sponsors funding option
- Create .github/FUNDING.yml to enable Sponsor button in repo sidebar
- Add Support section to README with sponsor link

Closes #46
2026-02-11 20:36:05 +01:00
Tommy D. Rossi 674e7c73ad release: @xmorse/playwright-core@1.59.3, playwriter@0.0.59
Fix 'Cannot find module graceful-fs' error on clean npx playwriter installs.

@xmorse/playwright-core@1.59.3:
- Add missing runtime dependencies: graceful-fs, retry, signal-exit
- Fixes GitHub #45

playwriter@0.0.59:
- Update @xmorse/playwright-core to 1.59.3
2026-02-11 19:28:08 +01:00
Tommy D. Rossi b60bbdd052 release: playwriter@0.0.58 2026-02-11 16:51:47 +01:00
Tommy D. Rossi e9e9c2de08 agents 2026-02-11 16:43:36 +01:00
Tommy D. Rossi e0eba5cf77 release: playwriter@0.0.57 2026-02-11 16:35:43 +01:00
Tommy D. Rossi 5098154585 docs: avoid focus stealing in framer iframe workflow
Document the page-reuse pattern for the Framer project tab so repeated checks do not spawn duplicate tabs, and explicitly ban bringToFront in this guide to avoid interrupting the active browser window during local debugging.
2026-02-11 16:16:39 +01:00
Tommy D. Rossi e6f1e8c089 fix extension runtime session routing for oopif iframe locators
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.
2026-02-11 16:15:02 +01:00
Tommy D. Rossi 1b6c66fed0 dist release 2026-02-07 18:32:03 +01:00
Tommy D. Rossi 150d252b59 fix: stabilize youtube navigation ready-state assertion
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.
2026-02-07 17:37:00 +01:00
Tommy D. Rossi eb36291212 Update AGENTS.md 2026-02-07 09:51:15 +01:00
Tommy D. Rossi be94434c3c Update PLAYWRITER_AGENTS.md 2026-02-07 09:51:10 +01:00
Tommy D. Rossi 5a3f344a0b Update manifest.json 2026-02-07 09:49:41 +01:00
Tommy D. Rossi 8ef3b21776 fix: prioritize test IDs over id attribute in aria snapshot refs
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.
2026-02-07 09:25:28 +01:00
Tommy D. Rossi e851adfcb6 refactor: replace CDPSession class with type-safe ICDPSession backed by Playwright's internal CDP session
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
2026-02-06 18:38:26 +01:00
Tommy D. Rossi 682bde5c14 Update PLAYWRITER_AGENTS.md 2026-02-06 18:03:58 +01:00
Tommy D. Rossi 87188423b1 feat: reuse Playwright's internal CDP session instead of creating new WebSocket connections
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
2026-02-06 18:03:26 +01:00
Tommy D. Rossi 78734bf1e8 fix: prevent tab group add/remove infinite loop when dragging tabs
When a user drags a tab into the playwriter group, connectTab() sets
its state to 'connecting'. syncTabGroup() previously only kept
'connected' tabs in the group, so it would immediately ungroup the
'connecting' tab. This ungroup fired chrome.tabs.onUpdated, which
called disconnectTab(). When the original connectTab() resolved, it
re-added the tab, triggering onUpdated again — creating an infinite
add/remove/add/remove loop.

Fix: include 'connecting' tabs in syncTabGroup's group membership
filter. This way syncTabGroup won't fight the user's intent by
removing tabs that are still being set up. Error-state tabs are
still correctly excluded from the group.
2026-02-06 15:39:35 +01:00
Tommy D. Rossi d6adb460d5 fix: move log dir from /tmp/playwriter to ~/.playwriter (fixes #44) 2026-02-06 10:44:28 +01:00
Tommy D. Rossi 7f3fddb2af chore: update pnpm-lock.yaml 2026-02-06 10:37:45 +01:00
Tommy D. Rossi 66cdb02dd9 refactor: simplify iframe handling - use frame: Frame instead of iframe: Locator 2026-02-06 10:32:39 +01:00
Tommy D. Rossi d3a87cab2c agents 2026-02-05 22:31:09 +01:00
Tommy D. Rossi 2a95cc9a9a Update playwright 2026-02-05 22:28:59 +01:00
Tommy D. Rossi d9c3362dd9 Update PLAYWRITER_AGENTS.md 2026-02-05 22:28:56 +01:00
Tommy D. Rossi 9e88bcd64e agents 2026-02-05 22:26:47 +01:00
Tommy D. Rossi 187b8ba992 Update AGENTS.md 2026-02-05 22:14:55 +01:00
Tommy D. Rossi 2bc851e37b Update PLAYWRITER_AGENTS.md 2026-02-05 22:14:35 +01:00
Tommy D. Rossi afd0840523 chore: wire forked playwright-core into workspace 2026-02-05 17:06:08 +01:00
Tommy D. Rossi 3f24ac1d8f fix: use Runtime.evaluate fallback when setScriptSource is deprecated
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.
2026-02-05 16:56:27 +01:00
Tommy D. Rossi 0eaf771c57 docs: refine Framer MCP palette verification 2026-02-05 15:48:17 +01:00