Files
remorses__playwriter/MEMORY.md
T
Tommy D. Rossi adc7125b47 fix: skip auto-printing Playwright handles in CLI execute output (#82)
When a single-expression code snippet auto-returns a Playwright object
(e.g. `await page.goto(url)` returning a Response), the CLI previously
called util.inspect() at depth 4 on the handle, producing 400K+ chars
that included every process.env variable (API keys, tokens, secrets).

Even with the playwright-core util.inspect fix (@xmorse/playwright-core@1.59.7),
these handles are useless output — they're programmatic references with no
display value. Agents and users get nothing meaningful from seeing:

  [return value] Response@response@abc123 { request: Channel<...>, ... }

Fix: add isPlaywrightChannelOwner() duck-type check in the executor return
value path. Handles are silently skipped (duck: _type + _guid + _connection).
The existing 'Code executed successfully (no output)' fallback applies when
there's no other output.

To see data, users must explicitly:
  return response.url()      → prints the URL string
  return response.status()   → prints 200
  console.log(response)      → prints safe summary via playwright-core

Also includes:
- Regression test (channel-owner-inspect.test.ts) for inspect handler
- isPlaywrightChannelOwner() unit tests (executor.unit.test.ts)
- Stale color-scheme snapshot fix (relay-core.test.ts)
- MEMORY.md with learnings (Node util.inspect bypass of Proxy traps)
2026-04-05 11:57:30 +02:00

3.3 KiB

Memory

Auto-returned values in playwriter CLI: skip useless Playwright handles (Apr 2026)

The CLI auto-returns single-expression code (e.g. await page.goto(url)). Playwright methods return handle objects (Response, Page, Browser, Locator) that are useless to print — they're programmatic references, not display data. The user does NOT want a hint message either — just silently skip.

Rule: if auto-returned value is a ChannelOwner (duck-type: _type + _guid + _connection), emit nothing. The Code executed successfully (no output) fallback handles the "nothing else printed" case.

User must explicitly console.log(response) or return specific fields (return response.url()) to see data. console.log still works safely via the playwright-core custom inspect handler.

Playwright browsers install is broken locally (Apr 2026)

pnpm exec playwright install fails with Command "playwright" not found because this repo uses @xmorse/playwright-core not playwright. The core CLI node playwright/packages/playwright-core/cli.js install chromium also fails with TypeError: onExit is not a function.

Workaround for pnpm test (which needs chromium-1209): symlink the already-installed 1208 build to 1209. Tests launchPersistentContext and work with the slightly older binary:

ln -sf ~/Library/Caches/ms-playwright/chromium-1208 \
       ~/Library/Caches/ms-playwright/chromium-1209
ln -sf ~/Library/Caches/ms-playwright/chromium_headless_shell-1208 \
       ~/Library/Caches/ms-playwright/chromium_headless_shell-1209

For standalone integration tests: pass executablePath: '/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing' to chromium.launch({ headless: true, executablePath }).

Node util.inspect bypasses Proxy traps (Apr 2026)

Node's util.inspect does NOT invoke Proxy get traps when looking up Symbol.for('nodejs.util.inspect.custom'). It reads the symbol property directly from the proxy target object.

To expose a custom inspect handler on a proxied object, the symbol MUST be set on the underlying target, not via the proxy's get trap:

// BAD — never fires
new Proxy(target, {
  get: (obj, prop) => {
    if (prop === Symbol.for('nodejs.util.inspect.custom'))
      return () => 'custom output'
    return obj[prop]
  },
})

// GOOD — works
target[Symbol.for('nodejs.util.inspect.custom')] = function () {
  return 'custom output'
}
const proxy = new Proxy(target, { get: (obj, prop) => obj[prop] })

Direct property access proxy[Symbol.for('nodejs.util.inspect.custom')] DOES go through the proxy get trap — only util.inspect's internal lookup bypasses it.

Playwright ChannelOwner leaks process.env via util.inspect (issue #82)

Playwright's ChannelOwner._connection._platform.env is set to process.env on node. At depth 4, util.inspect(response) traverses:

Response → _connection → _platform → env: { ALL_ENV_VARS }

This dumps every environment variable (API keys, tokens, passwords). Happened whenever a user auto-returned a Playwright object from the CLI, e.g. playwriter -s 1 -e 'await page.goto(url)'.

Fix: custom [Symbol.for('nodejs.util.inspect.custom')] on ChannelOwner prototype + on the channel proxy target. See playwright/packages/playwright-core/src/client/channelOwner.ts.