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/dist/bippy.js +1 -1
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +7 -11
- package/dist/cdp-relay.js.map +1 -1
- package/dist/cli.js +10 -11
- package/dist/cli.js.map +1 -1
- package/dist/executor.d.ts +0 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +4 -4
- package/dist/executor.js.map +1 -1
- package/dist/extension/background.js +2 -2
- package/dist/readability.js +1 -1
- package/dist/relay-session.test.js +1 -1
- package/dist/relay-session.test.js.map +1 -1
- package/dist/selector-generator.js +1 -1
- package/dist/utils.d.ts +2 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -4
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/cdp-relay.ts +6 -13
- package/src/cli.ts +10 -12
- package/src/executor.ts +4 -5
- package/src/relay-session.test.ts +1 -1
- package/src/utils.ts +4 -5
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
|
|
525
|
-
//
|
|
526
|
-
async function maybeAutoCreateInitialTab(
|
|
527
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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')
|