playwriter 0.0.63 → 0.0.89
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/a11y-client.js +18 -8
- package/dist/aria-snapshot.d.ts +41 -3
- package/dist/aria-snapshot.d.ts.map +1 -1
- package/dist/aria-snapshot.js +134 -55
- package/dist/aria-snapshot.js.map +1 -1
- package/dist/aria-snapshot.test.js +5 -2
- package/dist/aria-snapshot.test.js.map +1 -1
- package/dist/aria-snapshot.unit.test.js +83 -41
- package/dist/aria-snapshot.unit.test.js.map +1 -1
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +5 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +1 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +5 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +1 -0
- package/dist/bippy.js +1 -1
- package/dist/cdp-log.d.ts +1 -1
- package/dist/cdp-log.d.ts.map +1 -1
- package/dist/cdp-log.js +1 -1
- package/dist/cdp-log.js.map +1 -1
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +492 -298
- package/dist/cdp-relay.js.map +1 -1
- package/dist/cdp-session.d.ts.map +1 -1
- package/dist/cdp-session.js.map +1 -1
- package/dist/cdp-types.d.ts.map +1 -1
- package/dist/cdp-types.js +7 -7
- package/dist/cdp-types.js.map +1 -1
- package/dist/clean-html.d.ts.map +1 -1
- package/dist/clean-html.js +4 -5
- package/dist/clean-html.js.map +1 -1
- package/dist/cli.js +45 -27
- package/dist/cli.js.map +1 -1
- package/dist/create-logger.d.ts.map +1 -1
- package/dist/create-logger.js +3 -1
- package/dist/create-logger.js.map +1 -1
- package/dist/debugger-examples-types.d.ts.map +1 -1
- package/dist/debugger.d.ts.map +1 -1
- package/dist/debugger.js +1 -3
- package/dist/debugger.js.map +1 -1
- package/dist/diff-utils.d.ts.map +1 -1
- package/dist/diff-utils.js +1 -4
- package/dist/diff-utils.js.map +1 -1
- package/dist/editor-api.md +12 -2
- package/dist/editor-examples.d.ts +1 -1
- package/dist/editor-examples.d.ts.map +1 -1
- package/dist/editor-examples.js +1 -1
- package/dist/editor-examples.js.map +1 -1
- package/dist/editor.d.ts +1 -1
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +1 -1
- package/dist/editor.js.map +1 -1
- package/dist/executor.d.ts +26 -3
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +297 -64
- package/dist/executor.js.map +1 -1
- package/dist/executor.unit.test.js +38 -1
- package/dist/executor.unit.test.js.map +1 -1
- package/dist/extension-connection.test.js +139 -36
- package/dist/extension-connection.test.js.map +1 -1
- package/dist/ffmpeg.d.ts +148 -0
- package/dist/ffmpeg.d.ts.map +1 -0
- package/dist/ffmpeg.js +523 -0
- package/dist/ffmpeg.js.map +1 -0
- package/dist/ghost-browser.d.ts.map +1 -1
- package/dist/ghost-browser.js.map +1 -1
- package/dist/ghost-cursor-client.js +287 -0
- package/dist/ghost-cursor.d.ts +27 -0
- package/dist/ghost-cursor.d.ts.map +1 -0
- package/dist/ghost-cursor.js +63 -0
- package/dist/ghost-cursor.js.map +1 -0
- package/dist/htmlrewrite.d.ts.map +1 -1
- package/dist/htmlrewrite.js +17 -55
- package/dist/htmlrewrite.js.map +1 -1
- package/dist/htmlrewrite.test.js.map +1 -1
- package/dist/kill-port.d.ts.map +1 -1
- package/dist/kill-port.js +1 -3
- package/dist/kill-port.js.map +1 -1
- package/dist/locator-selector.test.d.ts +2 -0
- package/dist/locator-selector.test.d.ts.map +1 -0
- package/dist/locator-selector.test.js +96 -0
- package/dist/locator-selector.test.js.map +1 -0
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +8 -3
- package/dist/mcp.js.map +1 -1
- package/dist/on-mouse-action.test.d.ts +2 -0
- package/dist/on-mouse-action.test.d.ts.map +1 -0
- package/dist/on-mouse-action.test.js +155 -0
- package/dist/on-mouse-action.test.js.map +1 -0
- package/dist/page-markdown.js +4 -4
- package/dist/page-markdown.js.map +1 -1
- package/dist/prompt.md +450 -377
- package/dist/protocol.d.ts +4 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/readability.js +16 -2
- package/dist/recording-ghost-cursor.d.ts +41 -0
- package/dist/recording-ghost-cursor.d.ts.map +1 -0
- package/dist/recording-ghost-cursor.js +79 -0
- package/dist/recording-ghost-cursor.js.map +1 -0
- package/dist/recording-relay.d.ts.map +1 -1
- package/dist/recording-relay.js +8 -8
- package/dist/recording-relay.js.map +1 -1
- package/dist/relay-client.d.ts +17 -4
- package/dist/relay-client.d.ts.map +1 -1
- package/dist/relay-client.js +45 -11
- package/dist/relay-client.js.map +1 -1
- package/dist/relay-core.test.d.ts.map +1 -1
- package/dist/relay-core.test.js +515 -26
- package/dist/relay-core.test.js.map +1 -1
- package/dist/relay-navigation.test.d.ts.map +1 -1
- package/dist/relay-navigation.test.js +169 -31
- package/dist/relay-navigation.test.js.map +1 -1
- package/dist/relay-session.test.d.ts.map +1 -1
- package/dist/relay-session.test.js +113 -65
- package/dist/relay-session.test.js.map +1 -1
- package/dist/relay-state.d.ts +158 -0
- package/dist/relay-state.d.ts.map +1 -0
- package/dist/relay-state.js +306 -0
- package/dist/relay-state.js.map +1 -0
- package/dist/relay-state.test.d.ts +2 -0
- package/dist/relay-state.test.d.ts.map +1 -0
- package/dist/relay-state.test.js +472 -0
- package/dist/relay-state.test.js.map +1 -0
- package/dist/scoped-fs.d.ts.map +1 -1
- package/dist/scoped-fs.js.map +1 -1
- package/dist/screen-recording.d.ts +66 -4
- package/dist/screen-recording.d.ts.map +1 -1
- package/dist/screen-recording.js +150 -13
- package/dist/screen-recording.js.map +1 -1
- package/dist/screen-recording.test.d.ts +2 -0
- package/dist/screen-recording.test.d.ts.map +1 -0
- package/dist/screen-recording.test.js +102 -0
- package/dist/screen-recording.test.js.map +1 -0
- package/dist/selector-generator.js +1 -1
- package/dist/snapshot-tools.test.js +71 -28
- package/dist/snapshot-tools.test.js.map +1 -1
- package/dist/start-relay-server.d.ts +1 -1
- package/dist/start-relay-server.d.ts.map +1 -1
- package/dist/start-relay-server.js +1 -1
- package/dist/start-relay-server.js.map +1 -1
- package/dist/styles-api.md +8 -1
- package/dist/styles-examples.d.ts +1 -1
- package/dist/styles-examples.d.ts.map +1 -1
- package/dist/styles-examples.js +1 -1
- package/dist/styles-examples.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +1 -3
- package/dist/styles.js.map +1 -1
- package/dist/test-declarations.d.ts.map +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/test-utils.js +7 -5
- package/dist/test-utils.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/wait-for-page-load.d.ts.map +1 -1
- package/dist/wait-for-page-load.js +1 -1
- package/dist/wait-for-page-load.js.map +1 -1
- package/package.json +4 -3
- package/src/a11y-client.ts +5 -4
- package/src/aria-snapshot.test.ts +5 -2
- package/src/aria-snapshot.ts +306 -117
- package/src/aria-snapshot.unit.test.ts +199 -141
- package/src/aria-snapshots/github-interactive.txt +2 -0
- package/src/aria-snapshots/github-raw.txt +5 -1
- package/src/aria-snapshots/hackernews-interactive.txt +238 -241
- package/src/aria-snapshots/hackernews-raw.txt +265 -269
- package/src/assets/aria-labels-example.png +0 -0
- package/src/assets/aria-labels-github.png +0 -0
- package/src/assets/aria-labels-hacker-news.png +0 -0
- package/src/assets/aria-labels-old-reddit.png +0 -0
- package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +5 -0
- package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +18 -0
- package/src/cdp-log.ts +4 -1
- package/src/cdp-relay.ts +1059 -737
- package/src/cdp-session.ts +12 -3
- package/src/cdp-types.ts +51 -51
- package/src/clean-html.ts +4 -5
- package/src/cli.ts +82 -55
- package/src/create-logger.ts +5 -3
- package/src/debugger-examples-types.ts +4 -1
- package/src/debugger.ts +1 -5
- package/src/diff-utils.ts +2 -5
- package/src/editor-examples.ts +11 -1
- package/src/editor.ts +10 -2
- package/src/executor.ts +374 -73
- package/src/executor.unit.test.ts +48 -1
- package/src/extension-connection.test.ts +612 -488
- package/src/ffmpeg.ts +769 -0
- package/src/ghost-browser.ts +4 -6
- package/src/ghost-cursor-client.ts +369 -0
- package/src/ghost-cursor.ts +110 -0
- package/src/htmlrewrite.test.ts +6 -2
- package/src/htmlrewrite.ts +348 -386
- package/src/kill-port.ts +1 -3
- package/src/locator-selector.test.ts +115 -0
- package/src/mcp-client.ts +1 -1
- package/src/mcp.ts +21 -15
- package/src/on-mouse-action.test.ts +196 -0
- package/src/page-markdown.ts +7 -7
- package/src/protocol.ts +73 -57
- package/src/recording-ghost-cursor.ts +113 -0
- package/src/recording-relay.ts +20 -12
- package/src/relay-client.ts +85 -18
- package/src/relay-core.test.ts +1117 -578
- package/src/relay-navigation.test.ts +648 -483
- package/src/relay-session.test.ts +984 -929
- package/src/relay-state.test.ts +570 -0
- package/src/relay-state.ts +497 -0
- package/src/resource.md +21 -49
- package/src/scoped-fs.ts +9 -3
- package/src/screen-recording.test.ts +111 -0
- package/src/screen-recording.ts +256 -31
- package/src/skill.md +476 -396
- package/src/snapshot-tools.test.ts +580 -528
- package/src/snapshots/shadcn-ui-accessibility-full.md +8 -8
- package/src/snapshots/shadcn-ui-accessibility-interactive.md +8 -8
- package/src/start-relay-server.ts +14 -11
- package/src/styles-examples.ts +8 -1
- package/src/styles.ts +20 -21
- package/src/test-declarations.ts +6 -6
- package/src/test-utils.ts +104 -91
- package/src/utils.ts +2 -1
- package/src/wait-for-page-load.ts +6 -1
package/src/cdp-session.ts
CHANGED
|
@@ -14,9 +14,15 @@ export interface ICDPSession {
|
|
|
14
14
|
sessionId?: string | null,
|
|
15
15
|
): Promise<ProtocolMapping.Commands[K]['returnType']>
|
|
16
16
|
|
|
17
|
-
on<K extends keyof ProtocolMapping.Events>(
|
|
17
|
+
on<K extends keyof ProtocolMapping.Events>(
|
|
18
|
+
event: K,
|
|
19
|
+
callback: (params: ProtocolMapping.Events[K][0]) => void,
|
|
20
|
+
): unknown
|
|
18
21
|
|
|
19
|
-
off<K extends keyof ProtocolMapping.Events>(
|
|
22
|
+
off<K extends keyof ProtocolMapping.Events>(
|
|
23
|
+
event: K,
|
|
24
|
+
callback: (params: ProtocolMapping.Events[K][0]) => void,
|
|
25
|
+
): unknown
|
|
20
26
|
|
|
21
27
|
detach(): Promise<void>
|
|
22
28
|
getSessionId?(): string | null
|
|
@@ -46,7 +52,10 @@ export class PlaywrightCDPSessionAdapter implements ICDPSession {
|
|
|
46
52
|
return this
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
off<K extends keyof ProtocolMapping.Events>(
|
|
55
|
+
off<K extends keyof ProtocolMapping.Events>(
|
|
56
|
+
event: K,
|
|
57
|
+
callback: (params: ProtocolMapping.Events[K][0]) => void,
|
|
58
|
+
): this {
|
|
50
59
|
this._playwrightSession.off(event as never, callback as never)
|
|
51
60
|
return this
|
|
52
61
|
}
|
package/src/cdp-types.ts
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import type { Protocol } from 'devtools-protocol'
|
|
2
|
-
import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js'
|
|
1
|
+
import type { Protocol } from 'devtools-protocol'
|
|
2
|
+
import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js'
|
|
3
3
|
|
|
4
|
-
export type CDPCommandSource = 'playwriter'
|
|
4
|
+
export type CDPCommandSource = 'playwriter'
|
|
5
5
|
|
|
6
6
|
export type CDPCommandFor<T extends keyof ProtocolMapping.Commands> = {
|
|
7
|
-
id: number
|
|
8
|
-
sessionId?: string
|
|
9
|
-
method: T
|
|
10
|
-
params?: ProtocolMapping.Commands[T]['paramsType'][0]
|
|
11
|
-
source?: CDPCommandSource
|
|
12
|
-
}
|
|
7
|
+
id: number
|
|
8
|
+
sessionId?: string
|
|
9
|
+
method: T
|
|
10
|
+
params?: ProtocolMapping.Commands[T]['paramsType'][0]
|
|
11
|
+
source?: CDPCommandSource
|
|
12
|
+
}
|
|
13
13
|
|
|
14
14
|
export type CDPCommand = {
|
|
15
|
-
[K in keyof ProtocolMapping.Commands]: CDPCommandFor<K
|
|
16
|
-
}[keyof ProtocolMapping.Commands]
|
|
15
|
+
[K in keyof ProtocolMapping.Commands]: CDPCommandFor<K>
|
|
16
|
+
}[keyof ProtocolMapping.Commands]
|
|
17
17
|
|
|
18
18
|
export type CDPResponseFor<T extends keyof ProtocolMapping.Commands> = {
|
|
19
|
-
id: number
|
|
20
|
-
sessionId?: string
|
|
21
|
-
result?: ProtocolMapping.Commands[T]['returnType']
|
|
22
|
-
error?: { code?: number; message: string }
|
|
23
|
-
}
|
|
19
|
+
id: number
|
|
20
|
+
sessionId?: string
|
|
21
|
+
result?: ProtocolMapping.Commands[T]['returnType']
|
|
22
|
+
error?: { code?: number; message: string }
|
|
23
|
+
}
|
|
24
24
|
|
|
25
25
|
export type CDPResponse = {
|
|
26
|
-
[K in keyof ProtocolMapping.Commands]: CDPResponseFor<K
|
|
27
|
-
}[keyof ProtocolMapping.Commands]
|
|
26
|
+
[K in keyof ProtocolMapping.Commands]: CDPResponseFor<K>
|
|
27
|
+
}[keyof ProtocolMapping.Commands]
|
|
28
28
|
|
|
29
29
|
export type CDPEventFor<T extends keyof ProtocolMapping.Events> = {
|
|
30
|
-
method: T
|
|
31
|
-
sessionId?: string
|
|
32
|
-
params?: ProtocolMapping.Events[T][0]
|
|
33
|
-
}
|
|
30
|
+
method: T
|
|
31
|
+
sessionId?: string
|
|
32
|
+
params?: ProtocolMapping.Events[T][0]
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
export type CDPEvent = {
|
|
36
|
-
[K in keyof ProtocolMapping.Events]: CDPEventFor<K
|
|
37
|
-
}[keyof ProtocolMapping.Events]
|
|
36
|
+
[K in keyof ProtocolMapping.Events]: CDPEventFor<K>
|
|
37
|
+
}[keyof ProtocolMapping.Events]
|
|
38
38
|
|
|
39
39
|
export type CDPResponseBase = {
|
|
40
|
-
id: number
|
|
41
|
-
sessionId?: string
|
|
42
|
-
result?: unknown
|
|
43
|
-
error?: { code?: number; message: string }
|
|
44
|
-
}
|
|
40
|
+
id: number
|
|
41
|
+
sessionId?: string
|
|
42
|
+
result?: unknown
|
|
43
|
+
error?: { code?: number; message: string }
|
|
44
|
+
}
|
|
45
45
|
|
|
46
46
|
export type CDPEventBase = {
|
|
47
|
-
method: string
|
|
48
|
-
sessionId?: string
|
|
49
|
-
params?: unknown
|
|
50
|
-
}
|
|
47
|
+
method: string
|
|
48
|
+
sessionId?: string
|
|
49
|
+
params?: unknown
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
export type CDPMessage = CDPCommand | CDPResponse | CDPEvent
|
|
52
|
+
export type CDPMessage = CDPCommand | CDPResponse | CDPEvent
|
|
53
53
|
|
|
54
54
|
export type RelayServerEvents = {
|
|
55
55
|
'cdp:command': (data: { clientId: string; command: CDPCommand }) => void
|
|
@@ -57,14 +57,14 @@ export type RelayServerEvents = {
|
|
|
57
57
|
'cdp:response': (data: { clientId: string; response: CDPResponseBase; command: CDPCommand }) => void
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export { Protocol, ProtocolMapping }
|
|
60
|
+
export { Protocol, ProtocolMapping }
|
|
61
61
|
|
|
62
62
|
// types tests. to see if types are right with some simple examples
|
|
63
63
|
if (false as any) {
|
|
64
64
|
const browserVersionCommand = {
|
|
65
65
|
id: 1,
|
|
66
66
|
method: 'Browser.getVersion',
|
|
67
|
-
} satisfies CDPCommand
|
|
67
|
+
} satisfies CDPCommand
|
|
68
68
|
|
|
69
69
|
const browserVersionResponse = {
|
|
70
70
|
id: 1,
|
|
@@ -74,8 +74,8 @@ if (false as any) {
|
|
|
74
74
|
revision: '123',
|
|
75
75
|
userAgent: 'Mozilla/5.0',
|
|
76
76
|
jsVersion: 'V8',
|
|
77
|
-
}
|
|
78
|
-
} satisfies CDPResponse
|
|
77
|
+
},
|
|
78
|
+
} satisfies CDPResponse
|
|
79
79
|
|
|
80
80
|
const targetAttachCommand = {
|
|
81
81
|
id: 2,
|
|
@@ -83,13 +83,13 @@ if (false as any) {
|
|
|
83
83
|
params: {
|
|
84
84
|
autoAttach: true,
|
|
85
85
|
waitForDebuggerOnStart: false,
|
|
86
|
-
}
|
|
87
|
-
} satisfies CDPCommand
|
|
86
|
+
},
|
|
87
|
+
} satisfies CDPCommand
|
|
88
88
|
|
|
89
89
|
const targetAttachResponse = {
|
|
90
90
|
id: 2,
|
|
91
91
|
result: undefined,
|
|
92
|
-
} satisfies CDPResponse
|
|
92
|
+
} satisfies CDPResponse
|
|
93
93
|
|
|
94
94
|
const attachedToTargetEvent = {
|
|
95
95
|
method: 'Target.attachedToTarget',
|
|
@@ -104,8 +104,8 @@ if (false as any) {
|
|
|
104
104
|
canAccessOpener: false,
|
|
105
105
|
},
|
|
106
106
|
waitingForDebugger: false,
|
|
107
|
-
}
|
|
108
|
-
} satisfies CDPEvent
|
|
107
|
+
},
|
|
108
|
+
} satisfies CDPEvent
|
|
109
109
|
|
|
110
110
|
const consoleMessageEvent = {
|
|
111
111
|
method: 'Runtime.consoleAPICalled',
|
|
@@ -114,23 +114,23 @@ if (false as any) {
|
|
|
114
114
|
args: [],
|
|
115
115
|
executionContextId: 1,
|
|
116
116
|
timestamp: 123456789,
|
|
117
|
-
}
|
|
118
|
-
} satisfies CDPEvent
|
|
117
|
+
},
|
|
118
|
+
} satisfies CDPEvent
|
|
119
119
|
|
|
120
120
|
const pageNavigateCommand = {
|
|
121
121
|
id: 3,
|
|
122
122
|
method: 'Page.navigate',
|
|
123
123
|
params: {
|
|
124
124
|
url: 'https://example.com',
|
|
125
|
-
}
|
|
126
|
-
} satisfies CDPCommand
|
|
125
|
+
},
|
|
126
|
+
} satisfies CDPCommand
|
|
127
127
|
|
|
128
128
|
const pageNavigateResponse = {
|
|
129
129
|
id: 3,
|
|
130
130
|
result: {
|
|
131
131
|
frameId: 'frame-1',
|
|
132
|
-
}
|
|
133
|
-
} satisfies CDPResponse
|
|
132
|
+
},
|
|
133
|
+
} satisfies CDPResponse
|
|
134
134
|
|
|
135
135
|
const networkRequestEvent = {
|
|
136
136
|
method: 'Network.requestWillBeSent',
|
|
@@ -153,6 +153,6 @@ if (false as any) {
|
|
|
153
153
|
},
|
|
154
154
|
redirectHasExtraInfo: false,
|
|
155
155
|
type: 'XHR',
|
|
156
|
-
}
|
|
157
|
-
} satisfies CDPEvent
|
|
156
|
+
},
|
|
157
|
+
} satisfies CDPEvent
|
|
158
158
|
}
|
package/src/clean-html.ts
CHANGED
|
@@ -26,17 +26,16 @@ function isRegExp(value: any): value is RegExp {
|
|
|
26
26
|
|
|
27
27
|
function getSnapshotKey(locator: Locator | Page): string {
|
|
28
28
|
if (isPage(locator)) {
|
|
29
|
-
return '
|
|
29
|
+
return 'page'
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
return (locator as any)._selector || '__locator__'
|
|
31
|
+
return `locator:${locator.selector()}`
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
export async function getCleanHTML(options: GetCleanHTMLOptions): Promise<string> {
|
|
36
35
|
const {
|
|
37
36
|
locator,
|
|
38
37
|
search,
|
|
39
|
-
showDiffSinceLastCall =
|
|
38
|
+
showDiffSinceLastCall = !search,
|
|
40
39
|
includeStyles = false,
|
|
41
40
|
maxAttrLen = 200,
|
|
42
41
|
maxContentLen = 500,
|
|
@@ -76,7 +75,7 @@ export async function getCleanHTML(options: GetCleanHTMLOptions): Promise<string
|
|
|
76
75
|
const previousSnapshot = pageSnapshots.get(snapshotKey)
|
|
77
76
|
pageSnapshots.set(snapshotKey, htmlStr)
|
|
78
77
|
|
|
79
|
-
//
|
|
78
|
+
// Diff defaults off when search is provided, but agent can explicitly enable both
|
|
80
79
|
if (showDiffSinceLastCall && previousSnapshot) {
|
|
81
80
|
const diffResult = createSmartDiff({
|
|
82
81
|
oldContent: previousSnapshot,
|
package/src/cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'node:path'
|
|
|
5
5
|
import util from 'node:util'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
7
|
import { cac } from '@xmorse/cac'
|
|
8
|
+
import pc from 'picocolors'
|
|
8
9
|
|
|
9
10
|
// Prevent Buffers from dumping hex bytes in util.inspect output.
|
|
10
11
|
Buffer.prototype[util.inspect.custom] = function () {
|
|
@@ -12,7 +13,14 @@ Buffer.prototype[util.inspect.custom] = function () {
|
|
|
12
13
|
}
|
|
13
14
|
import { killPortProcess } from './kill-port.js'
|
|
14
15
|
import { VERSION, LOG_FILE_PATH, LOG_CDP_FILE_PATH, parseRelayHost } from './utils.js'
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
ensureRelayServer,
|
|
18
|
+
RELAY_PORT,
|
|
19
|
+
waitForConnectedExtensions,
|
|
20
|
+
getExtensionOutdatedWarning,
|
|
21
|
+
getExtensionStatus,
|
|
22
|
+
type ExtensionStatus,
|
|
23
|
+
} from './relay-client.js'
|
|
16
24
|
|
|
17
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
18
26
|
|
|
@@ -20,15 +28,6 @@ const cliRelayEnv = { PLAYWRITER_AUTO_ENABLE: '1' }
|
|
|
20
28
|
|
|
21
29
|
const cli = cac('playwriter')
|
|
22
30
|
|
|
23
|
-
type ExtensionStatus = {
|
|
24
|
-
extensionId: string
|
|
25
|
-
stableKey?: string
|
|
26
|
-
browser: string | null
|
|
27
|
-
profile: { email: string; id: string } | null
|
|
28
|
-
activeTargets: number
|
|
29
|
-
playwriterVersion?: string | null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
31
|
cli
|
|
33
32
|
.command('', 'Start the MCP server or controls the browser with -e')
|
|
34
33
|
.option('--host <host>', 'Remote relay server host to connect to (or use PLAYWRITER_HOST env var)')
|
|
@@ -76,7 +75,7 @@ async function fetchExtensionsStatus(host?: string): Promise<ExtensionStatus[]>
|
|
|
76
75
|
if (!fallback.ok) {
|
|
77
76
|
return []
|
|
78
77
|
}
|
|
79
|
-
const fallbackData = await fallback.json() as {
|
|
78
|
+
const fallbackData = (await fallback.json()) as {
|
|
80
79
|
connected: boolean
|
|
81
80
|
activeTargets: number
|
|
82
81
|
browser: string | null
|
|
@@ -86,16 +85,18 @@ async function fetchExtensionsStatus(host?: string): Promise<ExtensionStatus[]>
|
|
|
86
85
|
if (!fallbackData?.connected) {
|
|
87
86
|
return []
|
|
88
87
|
}
|
|
89
|
-
return [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
extensionId: 'default',
|
|
91
|
+
stableKey: undefined,
|
|
92
|
+
browser: fallbackData?.browser,
|
|
93
|
+
profile: fallbackData?.profile,
|
|
94
|
+
activeTargets: fallbackData?.activeTargets,
|
|
95
|
+
playwriterVersion: fallbackData?.playwriterVersion || null,
|
|
96
|
+
},
|
|
97
|
+
]
|
|
97
98
|
}
|
|
98
|
-
const data = await response.json() as {
|
|
99
|
+
const data = (await response.json()) as {
|
|
99
100
|
extensions: ExtensionStatus[]
|
|
100
101
|
}
|
|
101
102
|
return data?.extensions || []
|
|
@@ -128,8 +129,12 @@ async function executeCode(options: {
|
|
|
128
129
|
if (!host && !process.env.PLAYWRITER_HOST) {
|
|
129
130
|
const restarted = await ensureRelayServer({ logger: console, env: cliRelayEnv })
|
|
130
131
|
if (restarted) {
|
|
131
|
-
const
|
|
132
|
-
|
|
132
|
+
const connectedExtensions = await waitForConnectedExtensions({
|
|
133
|
+
logger: console,
|
|
134
|
+
timeoutMs: 10000,
|
|
135
|
+
pollIntervalMs: 250,
|
|
136
|
+
})
|
|
137
|
+
if (connectedExtensions.length === 0) {
|
|
133
138
|
console.error('Warning: Extension not connected. Commands may fail.')
|
|
134
139
|
}
|
|
135
140
|
}
|
|
@@ -150,7 +155,9 @@ async function executeCode(options: {
|
|
|
150
155
|
method: 'POST',
|
|
151
156
|
headers: {
|
|
152
157
|
'Content-Type': 'application/json',
|
|
153
|
-
...(token || process.env.PLAYWRITER_TOKEN
|
|
158
|
+
...(token || process.env.PLAYWRITER_TOKEN
|
|
159
|
+
? { Authorization: `Bearer ${token || process.env.PLAYWRITER_TOKEN}` }
|
|
160
|
+
: {}),
|
|
154
161
|
},
|
|
155
162
|
body: JSON.stringify({ sessionId, code, timeout, cwd }),
|
|
156
163
|
})
|
|
@@ -161,7 +168,11 @@ async function executeCode(options: {
|
|
|
161
168
|
process.exit(1)
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
const result = await response.json() as {
|
|
171
|
+
const result = (await response.json()) as {
|
|
172
|
+
text: string
|
|
173
|
+
images: Array<{ data: string; mimeType: string }>
|
|
174
|
+
isError: boolean
|
|
175
|
+
}
|
|
165
176
|
|
|
166
177
|
// Print output
|
|
167
178
|
if (result.text) {
|
|
@@ -198,16 +209,30 @@ cli
|
|
|
198
209
|
.option('--host <host>', 'Remote relay server host')
|
|
199
210
|
.option('--browser <stableKey>', 'Stable browser key when multiple browsers are connected')
|
|
200
211
|
.action(async (options: { host?: string; browser?: string }) => {
|
|
201
|
-
|
|
212
|
+
const isLocal = !options.host && !process.env.PLAYWRITER_HOST
|
|
213
|
+
|
|
214
|
+
let extensions: ExtensionStatus[] = []
|
|
215
|
+
|
|
216
|
+
if (isLocal) {
|
|
202
217
|
await ensureRelayServer({ logger: console, env: cliRelayEnv })
|
|
203
|
-
await
|
|
204
|
-
timeoutMs:
|
|
218
|
+
extensions = await waitForConnectedExtensions({
|
|
219
|
+
timeoutMs: 12000,
|
|
220
|
+
pollIntervalMs: 250,
|
|
205
221
|
logger: console,
|
|
206
222
|
})
|
|
207
|
-
}
|
|
208
223
|
|
|
224
|
+
if (extensions.length === 0) {
|
|
225
|
+
console.log(pc.dim('Waiting briefly for extension to reconnect...'))
|
|
226
|
+
extensions = await waitForConnectedExtensions({
|
|
227
|
+
timeoutMs: 10000,
|
|
228
|
+
pollIntervalMs: 250,
|
|
229
|
+
logger: console,
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
extensions = await fetchExtensionsStatus(options.host)
|
|
234
|
+
}
|
|
209
235
|
|
|
210
|
-
const extensions = await fetchExtensionsStatus(options.host)
|
|
211
236
|
if (extensions.length === 0) {
|
|
212
237
|
console.error('No connected browsers detected. Click the Playwriter extension icon.')
|
|
213
238
|
process.exit(1)
|
|
@@ -253,9 +278,10 @@ cli
|
|
|
253
278
|
|
|
254
279
|
try {
|
|
255
280
|
const serverUrl = await getServerUrl(options.host)
|
|
256
|
-
const extensionId =
|
|
257
|
-
|
|
258
|
-
|
|
281
|
+
const extensionId =
|
|
282
|
+
selectedExtension.extensionId === 'default'
|
|
283
|
+
? null
|
|
284
|
+
: selectedExtension.stableKey || selectedExtension.extensionId
|
|
259
285
|
const cwd = process.cwd()
|
|
260
286
|
const response = await fetch(`${serverUrl}/cli/session/new`, {
|
|
261
287
|
method: 'POST',
|
|
@@ -267,7 +293,7 @@ cli
|
|
|
267
293
|
console.error(`Error: ${response.status} ${text}`)
|
|
268
294
|
process.exit(1)
|
|
269
295
|
}
|
|
270
|
-
const result = await response.json() as { id: string; extensionId: string | null }
|
|
296
|
+
const result = (await response.json()) as { id: string; extensionId: string | null }
|
|
271
297
|
console.log(`Session ${result.id} created. Use with: playwriter -s ${result.id} -e "..."`)
|
|
272
298
|
} catch (error: any) {
|
|
273
299
|
console.error(`Error: ${error.message}`)
|
|
@@ -300,7 +326,7 @@ cli
|
|
|
300
326
|
console.error(`Error: ${response.status} ${await response.text()}`)
|
|
301
327
|
process.exit(1)
|
|
302
328
|
}
|
|
303
|
-
const result = await response.json() as {
|
|
329
|
+
const result = (await response.json()) as {
|
|
304
330
|
sessions: Array<{
|
|
305
331
|
id: string
|
|
306
332
|
stateKeys: string[]
|
|
@@ -335,7 +361,7 @@ cli
|
|
|
335
361
|
' ' +
|
|
336
362
|
'EXT'.padEnd(extensionWidth) +
|
|
337
363
|
' ' +
|
|
338
|
-
'STATE KEYS'
|
|
364
|
+
'STATE KEYS',
|
|
339
365
|
)
|
|
340
366
|
console.log('-'.repeat(idWidth + browserWidth + profileWidth + extensionWidth + stateWidth + 8))
|
|
341
367
|
|
|
@@ -351,7 +377,7 @@ cli
|
|
|
351
377
|
' ' +
|
|
352
378
|
(session.extensionId || '-').padEnd(extensionWidth) +
|
|
353
379
|
' ' +
|
|
354
|
-
stateStr
|
|
380
|
+
stateStr,
|
|
355
381
|
)
|
|
356
382
|
}
|
|
357
383
|
})
|
|
@@ -374,7 +400,7 @@ cli
|
|
|
374
400
|
})
|
|
375
401
|
|
|
376
402
|
if (!response.ok) {
|
|
377
|
-
const result = await response.json() as { error: string }
|
|
403
|
+
const result = (await response.json()) as { error: string }
|
|
378
404
|
console.error(`Error: ${result.error}`)
|
|
379
405
|
process.exit(1)
|
|
380
406
|
}
|
|
@@ -410,8 +436,10 @@ cli
|
|
|
410
436
|
process.exit(1)
|
|
411
437
|
}
|
|
412
438
|
|
|
413
|
-
const result = await response.json() as { success: boolean; pageUrl: string; pagesCount: number }
|
|
414
|
-
console.log(
|
|
439
|
+
const result = (await response.json()) as { success: boolean; pageUrl: string; pagesCount: number }
|
|
440
|
+
console.log(
|
|
441
|
+
`Connection reset successfully. ${result.pagesCount} page(s) available. Current page URL: ${result.pageUrl}`,
|
|
442
|
+
)
|
|
415
443
|
} catch (error: any) {
|
|
416
444
|
console.error(`Error: ${error.message}`)
|
|
417
445
|
process.exit(1)
|
|
@@ -419,9 +447,12 @@ cli
|
|
|
419
447
|
})
|
|
420
448
|
|
|
421
449
|
cli
|
|
422
|
-
.command(
|
|
423
|
-
|
|
424
|
-
|
|
450
|
+
.command(
|
|
451
|
+
'serve',
|
|
452
|
+
`Start the relay server on this machine (must be the same host where Chrome is running). Remote clients (Docker, other machines) connect via PLAYWRITER_HOST. Use --host localhost for Docker (no token needed) — containers reach it via host.docker.internal. Use --host 0.0.0.0 for LAN/internet access (requires --token).`,
|
|
453
|
+
)
|
|
454
|
+
.option('--host <host>', 'Host to bind to (use "localhost" for Docker, "0.0.0.0" for remote access)', { default: '0.0.0.0' })
|
|
455
|
+
.option('--token <token>', 'Authentication token, required when --host is 0.0.0.0 (or use PLAYWRITER_TOKEN env var)')
|
|
425
456
|
.option('--replace', 'Kill existing server if running')
|
|
426
457
|
.action(async (options: { host: string; token?: string; replace?: boolean }) => {
|
|
427
458
|
const token = options.token || process.env.PLAYWRITER_TOKEN
|
|
@@ -512,20 +543,16 @@ cli
|
|
|
512
543
|
})
|
|
513
544
|
})
|
|
514
545
|
|
|
515
|
-
cli
|
|
516
|
-
.
|
|
517
|
-
.
|
|
518
|
-
|
|
519
|
-
console.log(`cdp: ${LOG_CDP_FILE_PATH}`)
|
|
520
|
-
})
|
|
546
|
+
cli.command('logfile', 'Print the path to the relay server log file').action(() => {
|
|
547
|
+
console.log(`relay: ${LOG_FILE_PATH}`)
|
|
548
|
+
console.log(`cdp: ${LOG_CDP_FILE_PATH}`)
|
|
549
|
+
})
|
|
521
550
|
|
|
522
|
-
cli
|
|
523
|
-
.
|
|
524
|
-
.
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
console.log(content)
|
|
528
|
-
})
|
|
551
|
+
cli.command('skill', 'Print the full playwriter usage instructions').action(() => {
|
|
552
|
+
const skillPath = path.join(__dirname, '..', 'src', 'skill.md')
|
|
553
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
554
|
+
console.log(content)
|
|
555
|
+
})
|
|
529
556
|
|
|
530
557
|
cli.help()
|
|
531
558
|
cli.version(VERSION)
|
package/src/create-logger.ts
CHANGED
|
@@ -21,9 +21,11 @@ export function createFileLogger({ logFilePath }: { logFilePath?: string } = {})
|
|
|
21
21
|
let queue: Promise<void> = Promise.resolve()
|
|
22
22
|
|
|
23
23
|
const log = (...args: unknown[]): Promise<void> => {
|
|
24
|
-
const message = args
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const message = args
|
|
25
|
+
.map((arg) =>
|
|
26
|
+
typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false, maxStringLength: 1000 }),
|
|
27
|
+
)
|
|
28
|
+
.join(' ')
|
|
27
29
|
queue = queue.then(() => fs.promises.appendFile(resolvedLogFilePath, stripAnsi(message) + '\n'))
|
|
28
30
|
return queue
|
|
29
31
|
}
|
|
@@ -8,6 +8,9 @@ export declare const page: Page
|
|
|
8
8
|
export declare const getCDPSession: (options: { page: Page }) => Promise<ICDPSession>
|
|
9
9
|
export declare const createDebugger: (options: { cdp: ICDPSession }) => Debugger
|
|
10
10
|
export declare const createEditor: (options: { cdp: ICDPSession }) => Editor
|
|
11
|
-
export declare const getStylesForLocator: (options: {
|
|
11
|
+
export declare const getStylesForLocator: (options: {
|
|
12
|
+
locator: Locator
|
|
13
|
+
includeUserAgentStyles?: boolean
|
|
14
|
+
}) => Promise<StylesResult>
|
|
12
15
|
export declare const formatStylesAsText: (styles: StylesResult) => string
|
|
13
16
|
export declare const console: { log: (...args: unknown[]) => void }
|
package/src/debugger.ts
CHANGED
|
@@ -24,8 +24,6 @@ export interface EvaluateResult {
|
|
|
24
24
|
value: unknown
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
27
|
export interface ScriptInfo {
|
|
30
28
|
scriptId: string
|
|
31
29
|
url: string
|
|
@@ -533,9 +531,7 @@ export class Debugger {
|
|
|
533
531
|
async listScripts({ search }: { search?: string } = {}): Promise<ScriptInfo[]> {
|
|
534
532
|
await this.enable()
|
|
535
533
|
const scripts = Array.from(this.scripts.values())
|
|
536
|
-
const filtered = search
|
|
537
|
-
? scripts.filter((s) => s.url.toLowerCase().includes(search.toLowerCase()))
|
|
538
|
-
: scripts
|
|
534
|
+
const filtered = search ? scripts.filter((s) => s.url.toLowerCase().includes(search.toLowerCase())) : scripts
|
|
539
535
|
return filtered.slice(0, 20)
|
|
540
536
|
}
|
|
541
537
|
|
package/src/diff-utils.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface CreateSmartDiffOptions {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Creates a smart diff that returns full content when changes exceed threshold.
|
|
19
|
-
*
|
|
19
|
+
*
|
|
20
20
|
* When more than `threshold` (default 50%) of lines have changed, showing a diff
|
|
21
21
|
* is not useful - we return the full new content instead.
|
|
22
22
|
*/
|
|
@@ -54,10 +54,7 @@ export function createSmartDiff(options: CreateSmartDiffOptions): SmartDiffResul
|
|
|
54
54
|
const changeRatio = Math.min(changedLines / maxLines, 1) // Cap at 100%
|
|
55
55
|
|
|
56
56
|
// Build unified diff string from structured patch
|
|
57
|
-
const diffLines: string[] = [
|
|
58
|
-
`--- ${label} (previous)`,
|
|
59
|
-
`+++ ${label} (current)`,
|
|
60
|
-
]
|
|
57
|
+
const diffLines: string[] = [`--- ${label} (previous)`, `+++ ${label} (current)`]
|
|
61
58
|
for (const hunk of patch.hunks) {
|
|
62
59
|
diffLines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`)
|
|
63
60
|
diffLines.push(...hunk.lines)
|
package/src/editor-examples.ts
CHANGED
|
@@ -145,4 +145,14 @@ async function searchStyles() {
|
|
|
145
145
|
console.log(matches)
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
export {
|
|
148
|
+
export {
|
|
149
|
+
listScripts,
|
|
150
|
+
readScript,
|
|
151
|
+
editScript,
|
|
152
|
+
searchScripts,
|
|
153
|
+
writeScript,
|
|
154
|
+
editInlineScript,
|
|
155
|
+
readStylesheet,
|
|
156
|
+
editStylesheet,
|
|
157
|
+
searchStyles,
|
|
158
|
+
}
|
package/src/editor.ts
CHANGED
|
@@ -294,7 +294,7 @@ export class Editor {
|
|
|
294
294
|
private async setSource(
|
|
295
295
|
id: { scriptId: string } | { styleSheetId: string },
|
|
296
296
|
content: string,
|
|
297
|
-
dryRun = false
|
|
297
|
+
dryRun = false,
|
|
298
298
|
): Promise<EditResult> {
|
|
299
299
|
if ('styleSheetId' in id) {
|
|
300
300
|
await this.cdp.send('CSS.setStyleSheetText', { styleSheetId: id.styleSheetId, text: content })
|
|
@@ -410,7 +410,15 @@ export class Editor {
|
|
|
410
410
|
* @param options.content - New content
|
|
411
411
|
* @param options.dryRun - If true, validate without applying (default false, only works for JS)
|
|
412
412
|
*/
|
|
413
|
-
async write({
|
|
413
|
+
async write({
|
|
414
|
+
url,
|
|
415
|
+
content,
|
|
416
|
+
dryRun = false,
|
|
417
|
+
}: {
|
|
418
|
+
url: string
|
|
419
|
+
content: string
|
|
420
|
+
dryRun?: boolean
|
|
421
|
+
}): Promise<EditResult> {
|
|
414
422
|
await this.enable()
|
|
415
423
|
const id = this.getIdByUrl(url)
|
|
416
424
|
return this.setSource(id, content, dryRun)
|