playwriter 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/cdp-relay.ts CHANGED
@@ -25,7 +25,7 @@ Buffer.prototype[util.inspect.custom] = function () {
25
25
  }
26
26
 
27
27
  import { EventEmitter } from 'node:events'
28
- import { VERSION, EXTENSION_IDS } from './utils.js'
28
+ import { VERSION, EXTENSION_IDS, shouldAutoEnablePlaywriter } from './utils.js'
29
29
  import { createCdpLogger, type CdpLogEntry, type CdpLogger } from './cdp-log.js'
30
30
  import { RecordingRelay } from './recording-relay.js'
31
31
  import { appendSessionToWsUrl } from './chrome-discovery.js'
@@ -521,11 +521,10 @@ export async function startPlayWriterCDPRelayServer({
521
521
  return recordingRelays.get(connId) || null
522
522
  }
523
523
 
524
- // Auto-create initial tab when PLAYWRITER_AUTO_ENABLE is set and no targets exist.
525
- // This allows Playwright to connect and immediately have a page to work with.
526
- async function maybeAutoCreateInitialTab(options: { extensionId: string; autoEnable: boolean }): Promise<void> {
527
- const { extensionId, autoEnable } = options
528
- if (!autoEnable && !process.env.PLAYWRITER_AUTO_ENABLE) {
524
+ // Auto-create an initial blank tab when no targets exist. Set
525
+ // PLAYWRITER_AUTO_ENABLE=false to require manually enabled tabs instead.
526
+ async function maybeAutoCreateInitialTab(extensionId: string): Promise<void> {
527
+ if (!shouldAutoEnablePlaywriter()) {
529
528
  return
530
529
  }
531
530
  const conn = getExtensionConnection(extensionId)
@@ -655,14 +654,12 @@ export async function startPlayWriterCDPRelayServer({
655
654
  params,
656
655
  sessionId,
657
656
  source,
658
- autoEnable,
659
657
  }: {
660
658
  extensionId: string | null
661
659
  method: CDPCommand['method'] | (string & {})
662
660
  params: CDPCommand['params']
663
661
  sessionId?: CDPCommand['sessionId']
664
662
  source?: CDPCommand['source']
665
- autoEnable: boolean
666
663
  }) {
667
664
  const conn = getExtensionConnection(extensionId)
668
665
  const connectedTargets = conn?.connectedTargets || new Map<string, relayState.ConnectedTarget>()
@@ -702,7 +699,7 @@ export async function startPlayWriterCDPRelayServer({
702
699
  break
703
700
  }
704
701
  if (conn) {
705
- await maybeAutoCreateInitialTab({ extensionId: conn.id, autoEnable })
702
+ await maybeAutoCreateInitialTab(conn.id)
706
703
  }
707
704
  // Forward auto-attach so Chrome emits iframe Target.attachedToTarget events.
708
705
  // Playwright relies on these (with parentFrameId) when reconnecting over CDP.
@@ -1112,7 +1109,6 @@ export async function startPlayWriterCDPRelayServer({
1112
1109
  const clientId = c.req.param('clientId') || 'default'
1113
1110
  const url = new URL(c.req.url, 'http://localhost')
1114
1111
  const requestedExtensionId = url.searchParams.get('extensionId')
1115
- const autoEnable = url.searchParams.get('autoEnable') === '1'
1116
1112
  // When extensionId is explicit, resolve directly. Otherwise use fallback which
1117
1113
  // handles single-extension and uniquely-active-extension cases (#52).
1118
1114
  const resolvedExtension = requestedExtensionId
@@ -1204,7 +1200,6 @@ export async function startPlayWriterCDPRelayServer({
1204
1200
  params,
1205
1201
  sessionId,
1206
1202
  source,
1207
- autoEnable,
1208
1203
  })
1209
1204
 
1210
1205
  if (method === 'Target.setAutoAttach' && !sessionId) {
@@ -1983,7 +1978,6 @@ export async function startPlayWriterCDPRelayServer({
1983
1978
  app.post('/cli/session/new', async (c) => {
1984
1979
  const body = (await c.req.json().catch(() => ({}))) as {
1985
1980
  extensionId?: string | null
1986
- autoEnable?: boolean
1987
1981
  cwd?: string
1988
1982
  /** Direct CDP WebSocket URL — bypasses extension, connects straight to Chrome */
1989
1983
  cdpEndpoint?: string
@@ -2037,7 +2031,6 @@ export async function startPlayWriterCDPRelayServer({
2037
2031
  const executor = manager.getExecutor({
2038
2032
  sessionId,
2039
2033
  cwd,
2040
- cdpConfig: { host: '127.0.0.1', port, token, extensionId: conn.stableKey, autoEnable: body.autoEnable === true },
2041
2034
  sessionMetadata: {
2042
2035
  extensionId: conn.stableKey,
2043
2036
  browser: conn.info.browser || null,
package/src/cli.ts CHANGED
@@ -27,8 +27,6 @@ import { discoverChromeInstances, resolveDirectInput, type DiscoveredInstance }
27
27
 
28
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
29
29
 
30
- const cliRelayEnv = { PLAYWRITER_AUTO_ENABLE: '1' }
31
-
32
30
  const cli = goke('playwriter')
33
31
 
34
32
  cli
@@ -53,7 +51,7 @@ cli
53
51
  import('./package-paths.js'),
54
52
  ])
55
53
 
56
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
54
+ await ensureRelayServer({ logger: console })
57
55
 
58
56
  const browserPath = resolveBrowserExecutablePath({ browserPath: binaryPath })
59
57
  const extensionPath = getBundledExtensionPath()
@@ -237,7 +235,7 @@ async function executeCode(options: {
237
235
 
238
236
  // Ensure relay server is running (only for local)
239
237
  if (!host && !process.env.PLAYWRITER_HOST) {
240
- const restarted = await ensureRelayServer({ logger: console, env: cliRelayEnv })
238
+ const restarted = await ensureRelayServer({ logger: console })
241
239
  if (restarted) {
242
240
  const connectedExtensions = await waitForConnectedExtensions({
243
241
  logger: console,
@@ -445,7 +443,7 @@ cli
445
443
  let extensions: ExtensionStatus[] = []
446
444
 
447
445
  if (isLocal) {
448
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
446
+ await ensureRelayServer({ logger: console })
449
447
  extensions = await waitForConnectedExtensions({
450
448
  timeoutMs: 12000,
451
449
  pollIntervalMs: 250,
@@ -492,7 +490,7 @@ cli
492
490
  const response = await fetch(`${serverUrl}/cli/session/new`, {
493
491
  method: 'POST',
494
492
  headers: buildAuthHeaders({ token: options.token, json: true }),
495
- body: JSON.stringify({ extensionId, cwd, autoEnable: true }),
493
+ body: JSON.stringify({ extensionId, cwd }),
496
494
  })
497
495
  if (!response.ok) {
498
496
  const text = await response.text()
@@ -551,7 +549,7 @@ cli
551
549
  const response = await fetch(`${serverUrl}/cli/session/new`, {
552
550
  method: 'POST',
553
551
  headers: buildAuthHeaders({ token: options.token, json: true }),
554
- body: JSON.stringify({ extensionId: selected.extensionId, cwd, autoEnable: true }),
552
+ body: JSON.stringify({ extensionId: selected.extensionId, cwd }),
555
553
  })
556
554
  if (!response.ok) {
557
555
  const text = await response.text()
@@ -577,7 +575,7 @@ cli
577
575
 
578
576
  async function ensureRelayForSessionCreation(isLocal: boolean): Promise<void> {
579
577
  if (isLocal) {
580
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
578
+ await ensureRelayServer({ logger: console })
581
579
  }
582
580
  }
583
581
 
@@ -661,7 +659,7 @@ cli
661
659
  .option('--token <token>', 'Authentication token (or use PLAYWRITER_TOKEN env var)')
662
660
  .action(async (options) => {
663
661
  if (!options.host && !process.env.PLAYWRITER_HOST) {
664
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
662
+ await ensureRelayServer({ logger: console })
665
663
  }
666
664
 
667
665
  const serverUrl = await getServerUrl(options.host)
@@ -754,7 +752,7 @@ cli
754
752
  const serverUrl = await getServerUrl(options.host)
755
753
 
756
754
  if (!options.host && !process.env.PLAYWRITER_HOST) {
757
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
755
+ await ensureRelayServer({ logger: console })
758
756
  }
759
757
 
760
758
  try {
@@ -786,7 +784,7 @@ cli
786
784
  const serverUrl = await getServerUrl(options.host)
787
785
 
788
786
  if (!options.host && !process.env.PLAYWRITER_HOST) {
789
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
787
+ await ensureRelayServer({ logger: console })
790
788
  }
791
789
 
792
790
  try {
@@ -926,7 +924,7 @@ cli
926
924
 
927
925
  // Start relay if local so the extension can connect, then fetch in parallel
928
926
  if (isLocal) {
929
- await ensureRelayServer({ logger: console, env: cliRelayEnv })
927
+ await ensureRelayServer({ logger: console })
930
928
  }
931
929
 
932
930
  const [extensions, directInstances] = await Promise.all([
package/src/executor.ts CHANGED
@@ -14,7 +14,7 @@ import { fileURLToPath } from 'node:url'
14
14
  import vm from 'node:vm'
15
15
  import * as acorn from 'acorn'
16
16
  import { createSmartDiff } from './diff-utils.js'
17
- import { getCdpUrl, parseRelayHost } from './utils.js'
17
+ import { getCdpUrl, parseRelayHost, shouldAutoEnablePlaywriter } from './utils.js'
18
18
  import { getExtensionOutdatedWarning } from './relay-client.js'
19
19
  import { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
20
20
  import { ICDPSession, getCDPSessionForPage } from './cdp-session.js'
@@ -150,7 +150,7 @@ const EXTENSION_NOT_CONNECTED_ERROR = `The Playwriter Chrome extension is not co
150
150
  2. Clicked the extension icon on a tab to enable it (or refreshed the page if just installed)`
151
151
 
152
152
  const NO_PAGES_AVAILABLE_ERROR =
153
- 'No Playwright pages are available. Enable Playwriter on a tab or set PLAYWRITER_AUTO_ENABLE=1 to auto-create one.'
153
+ 'No Playwright pages are available. Enable Playwriter on a tab or unset PLAYWRITER_AUTO_ENABLE=false to auto-create one.'
154
154
 
155
155
  const MAX_LOGS_PER_PAGE = 5000
156
156
 
@@ -227,7 +227,6 @@ export interface CdpConfig {
227
227
  port?: number
228
228
  token?: string
229
229
  extensionId?: string | null
230
- autoEnable?: boolean
231
230
  /** Direct CDP WebSocket URL — bypasses relay + extension, connects straight to Chrome */
232
231
  directCdpUrl?: string
233
232
  }
@@ -1418,7 +1417,7 @@ export class PlaywrightExecutor {
1418
1417
  }
1419
1418
  }
1420
1419
 
1421
- // When extension is connected but has no pages, auto-create only if PLAYWRITER_AUTO_ENABLE is set.
1420
+ // When extension is connected but has no pages, auto-create unless PLAYWRITER_AUTO_ENABLE=false disables it.
1422
1421
  // In direct CDP mode, always create a page (no extension check needed).
1423
1422
  private async ensurePageForContext(options: { context: BrowserContext; timeout: number }): Promise<Page> {
1424
1423
  const { context, timeout } = options
@@ -1440,7 +1439,7 @@ export class PlaywrightExecutor {
1440
1439
  throw new Error(EXTENSION_NOT_CONNECTED_ERROR)
1441
1440
  }
1442
1441
 
1443
- if (!process.env.PLAYWRITER_AUTO_ENABLE) {
1442
+ if (!shouldAutoEnablePlaywriter()) {
1444
1443
  const waitTimeoutMs = Math.min(timeout, 1000)
1445
1444
  const startTime = Date.now()
1446
1445
  while (Date.now() - startTime < waitTimeoutMs) {
@@ -1279,7 +1279,7 @@ describe('Auto-enable Tests', () => {
1279
1279
 
1280
1280
  const previousAutoEnable = process.env.PLAYWRITER_AUTO_ENABLE
1281
1281
  delete process.env.PLAYWRITER_AUTO_ENABLE
1282
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT, autoEnable: true })).finally(() => {
1282
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT })).finally(() => {
1283
1283
  if (previousAutoEnable === undefined) {
1284
1284
  delete process.env.PLAYWRITER_AUTO_ENABLE
1285
1285
  return
package/src/utils.ts CHANGED
@@ -36,13 +36,11 @@ export function getCdpUrl({
36
36
  host = '127.0.0.1',
37
37
  token,
38
38
  extensionId,
39
- autoEnable,
40
39
  }: {
41
40
  port?: number
42
41
  host?: string
43
42
  token?: string
44
43
  extensionId?: string | null
45
- autoEnable?: boolean
46
44
  } = {}) {
47
45
  const id = `${Math.random().toString(36).substring(2, 15)}_${Date.now()}`
48
46
  const params = new URLSearchParams()
@@ -52,15 +50,16 @@ export function getCdpUrl({
52
50
  if (extensionId) {
53
51
  params.set('extensionId', extensionId)
54
52
  }
55
- if (autoEnable) {
56
- params.set('autoEnable', '1')
57
- }
58
53
  const queryString = params.toString()
59
54
  const suffix = queryString ? `?${queryString}` : ''
60
55
  const { wsBaseUrl } = parseRelayHost(host, port)
61
56
  return `${wsBaseUrl}/cdp/${id}${suffix}`
62
57
  }
63
58
 
59
+ export function shouldAutoEnablePlaywriter(): boolean {
60
+ return process.env.PLAYWRITER_AUTO_ENABLE?.toLowerCase() !== 'false'
61
+ }
62
+
64
63
  // Use ~/.playwriter for logs so each OS user gets their own dir (avoids permission errors on shared machines, see #44)
65
64
  const LOG_BASE_DIR = path.join(os.homedir(), '.playwriter')
66
65
  export const LOG_FILE_PATH = process.env.PLAYWRITER_LOG_FILE_PATH || path.join(LOG_BASE_DIR, 'relay-server.log')