@midscene/web 1.12.4 → 1.12.5-beta-20260907092148.0
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/es/bridge-mode/io-client.mjs +1 -1
- package/dist/es/bridge-mode/io-server.mjs +5 -6
- package/dist/es/bridge-mode/io-server.mjs.map +1 -1
- package/dist/es/bridge-mode/page-browser-side.mjs +3 -3
- package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -1
- package/dist/es/cli.mjs +1 -1
- package/dist/es/playwright/agent.mjs +5 -2
- package/dist/es/playwright/ai-fixture.mjs +1 -1
- package/dist/es/playwright/ai-fixture.mjs.map +1 -1
- package/dist/es/playwright/index.mjs +2 -5
- package/dist/lib/bridge-mode/io-client.js +1 -1
- package/dist/lib/bridge-mode/io-server.js +4 -5
- package/dist/lib/bridge-mode/io-server.js.map +1 -1
- package/dist/lib/bridge-mode/page-browser-side.js +2 -2
- package/dist/lib/bridge-mode/page-browser-side.js.map +1 -1
- package/dist/lib/cli.js +1 -1
- package/dist/lib/playwright/agent.js +15 -3
- package/dist/lib/playwright/ai-fixture.js +3 -3
- package/dist/lib/playwright/ai-fixture.js.map +1 -1
- package/dist/lib/playwright/index.js +42 -26
- package/dist/lib/playwright/index.js.map +1 -1
- package/dist/types/playwright/agent.d.ts +5 -1
- package/dist/types/playwright/ai-fixture.d.ts +1 -1
- package/dist/types/playwright/index.d.ts +1 -5
- package/package.json +9 -4
|
@@ -4,7 +4,7 @@ import { getDebug } from "@midscene/shared/logger";
|
|
|
4
4
|
import { logMsg } from "@midscene/shared/utils";
|
|
5
5
|
import { Server } from "socket.io";
|
|
6
6
|
import { io } from "socket.io-client";
|
|
7
|
-
import { BridgeCallTimeout, BridgeErrorCodeNoClientConnected, BridgeEvent, BridgeSignalKill, DefaultBridgeServerPort } from "./common.mjs";
|
|
7
|
+
import { BridgeCallTimeout, BridgeErrorCodeNoClientConnected, BridgeEvent, BridgeSignalKill, DefaultBridgeServerHost, DefaultBridgeServerPort } from "./common.mjs";
|
|
8
8
|
function _define_property(obj, key, value) {
|
|
9
9
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
10
10
|
value: value,
|
|
@@ -21,7 +21,7 @@ function isTrustedOrigin(origin) {
|
|
|
21
21
|
if (origin.startsWith('chrome-extension://')) return true;
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
|
-
const killRunningServer = async (port, host =
|
|
24
|
+
const killRunningServer = async (port, host = DefaultBridgeServerHost)=>{
|
|
25
25
|
try {
|
|
26
26
|
const client = io(`ws://${host}:${port || DefaultBridgeServerPort}`, {
|
|
27
27
|
query: {
|
|
@@ -57,8 +57,7 @@ class BridgeServer {
|
|
|
57
57
|
httpServer.once('error', (err)=>{
|
|
58
58
|
reject(new Error(`Bridge Listening Error: ${err.message}`));
|
|
59
59
|
});
|
|
60
|
-
|
|
61
|
-
else httpServer.listen(this.port, this.host);
|
|
60
|
+
httpServer.listen(this.port, this.host);
|
|
62
61
|
this.io = new Server(httpServer, {
|
|
63
62
|
maxHttpBufferSize: 104857600,
|
|
64
63
|
pingTimeout: 60000
|
|
@@ -102,7 +101,7 @@ class BridgeServer {
|
|
|
102
101
|
logMsg('one client connected');
|
|
103
102
|
this.socket = socket;
|
|
104
103
|
const clientVersion = socket.handshake.query.version;
|
|
105
|
-
logMsg(`Bridge connected, cli-side version v1.12.
|
|
104
|
+
logMsg(`Bridge connected, cli-side version v1.12.5-beta-20260907092148.0, browser-side version v${clientVersion}`);
|
|
106
105
|
socket.on(BridgeEvent.CallResponse, (params)=>{
|
|
107
106
|
const id = params.id;
|
|
108
107
|
const response = params.response;
|
|
@@ -126,7 +125,7 @@ class BridgeServer {
|
|
|
126
125
|
setTimeout(()=>{
|
|
127
126
|
this.onConnect?.();
|
|
128
127
|
const payload = {
|
|
129
|
-
version: "1.12.
|
|
128
|
+
version: "1.12.5-beta-20260907092148.0"
|
|
130
129
|
};
|
|
131
130
|
socket.emit(BridgeEvent.Connected, payload);
|
|
132
131
|
Promise.resolve().then(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-mode/io-server.mjs","sources":["../../../src/bridge-mode/io-server.ts"],"sourcesContent":["import {\n type IncomingMessage,\n type ServerResponse,\n createServer,\n} from 'node:http';\nimport { sleep } from '@midscene/core/utils';\nimport { getDebug } from '@midscene/shared/logger';\nimport { logMsg } from '@midscene/shared/utils';\nimport { Server, type Socket as ServerSocket } from 'socket.io';\nimport { io as ClientIO } from 'socket.io-client';\n\nimport {\n type BridgeCall,\n type BridgeCallResponse,\n BridgeCallTimeout,\n type BridgeConnectedEventPayload,\n BridgeErrorCodeNoClientConnected,\n BridgeEvent,\n BridgeSignalKill,\n DefaultBridgeServerPort,\n} from './common';\n\nconst debug = getDebug('web:bridge:io-server');\n\ndeclare const __VERSION__: string;\n\n/**\n * Returns true if the given Origin header value is allowed to connect\n * to the bridge server. Trusted origins are:\n * - No Origin header (local Node.js processes like killRunningServer)\n * - chrome-extension:// origins (the Midscene Chrome extension)\n *\n * Browser pages (https://evil.com, etc.) are rejected to prevent\n * Cross-Site WebSocket Hijacking (CSWSH) attacks.\n */\nfunction isTrustedOrigin(origin: string | undefined): boolean {\n if (!origin) return true;\n if (origin.startsWith('chrome-extension://')) return true;\n return false;\n}\n\nexport const killRunningServer = async (port?: number, host = 'localhost') => {\n try {\n const client = ClientIO(`ws://${host}:${port || DefaultBridgeServerPort}`, {\n query: {\n [BridgeSignalKill]: 1,\n },\n });\n await sleep(300);\n await client.close();\n } catch (e) {\n debug('failed to kill port: %O', e);\n }\n};\n\n// ws server, this is where the request is sent\nexport class BridgeServer {\n private callId = 0;\n private io: Server | null = null;\n private socket: ServerSocket | null = null;\n private listeningTimeoutId: NodeJS.Timeout | null = null;\n private listeningTimerFlag = false;\n private connectionTipTimer: NodeJS.Timeout | null = null;\n public calls: Record<string, BridgeCall> = {};\n\n private connectionLost = false;\n private connectionLostReason = '';\n\n constructor(\n public host: string,\n public port: number,\n public onConnect?: () => void,\n public onDisconnect?: (reason: string) => void,\n public closeConflictServer?: boolean,\n ) {}\n\n async listen(\n opts: {\n timeout?: number | false;\n } = {},\n ): Promise<void> {\n const { timeout = 30000 } = opts;\n\n if (this.closeConflictServer) {\n await killRunningServer(this.port, this.host);\n }\n\n return new Promise((resolve, reject) => {\n if (this.listeningTimerFlag) {\n return reject(new Error('already listening'));\n }\n this.listeningTimerFlag = true;\n\n this.listeningTimeoutId = timeout\n ? setTimeout(() => {\n reject(\n new Error(\n `no extension connected after ${timeout}ms (${BridgeErrorCodeNoClientConnected})`,\n ),\n );\n }, timeout)\n : null;\n\n this.connectionTipTimer =\n !timeout || timeout > 3000\n ? setTimeout(() => {\n logMsg('waiting for bridge to connect...');\n }, 2000)\n : null;\n\n // Create HTTP server — no /bridge-auth endpoint needed since we use\n // pure Origin checking in the Socket.IO middleware below.\n const httpServer = createServer(\n (_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(404);\n res.end();\n },\n );\n\n // Set up HTTP server event listeners FIRST\n httpServer.once('listening', () => {\n resolve();\n });\n\n httpServer.once('error', (err: Error) => {\n reject(new Error(`Bridge Listening Error: ${err.message}`));\n });\n\n // Start listening BEFORE creating Socket.IO Server\n // When host is 127.0.0.1 (default), don't specify host to listen on all local interfaces (IPv4 + IPv6)\n // This ensures localhost resolves correctly in both IPv4 and IPv6 environments\n if (this.host === '127.0.0.1') {\n httpServer.listen(this.port);\n } else {\n httpServer.listen(this.port, this.host);\n }\n\n // Now create Socket.IO Server attached to the already-listening HTTP server\n this.io = new Server(httpServer, {\n maxHttpBufferSize: 100 * 1024 * 1024, // 100MB\n // Increase pingTimeout to tolerate Chrome MV3 Service Worker suspension.\n // The SW keepalive alarm fires every ~24s; default pingTimeout (20s) may\n // be too short if the SW is suspended between alarm pings.\n pingTimeout: 60000,\n });\n\n this.io.use((socket, next) => {\n // Reject connections from untrusted browser origins to prevent\n // Cross-Site WebSocket Hijacking (CSWSH) and unauthorized kill signals.\n // The browser automatically sets the Origin header on WebSocket\n // handshakes, and JavaScript cannot override it.\n const origin = socket.handshake.headers.origin;\n if (!isTrustedOrigin(origin)) {\n debug('connection rejected: untrusted origin=%s', origin);\n return next(new Error('origin not allowed'));\n }\n\n // Allow new connections to replace old ones (reconnection after\n // extension Stop→Start). If the old socket is already disconnected\n // or unresponsive, accept the new connection immediately.\n if (\n socket.handshake.url.includes(BridgeSignalKill) === false &&\n this.socket?.connected\n ) {\n return next(new Error('server already connected by another client'));\n }\n next();\n });\n\n this.io.on('connection', (socket) => {\n // check the connection url\n const url = socket.handshake.url;\n if (url.includes(BridgeSignalKill)) {\n console.warn('kill signal received, closing bridge server');\n return this.close();\n }\n\n this.connectionLost = false;\n this.connectionLostReason = '';\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.listeningTimeoutId = null;\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n this.connectionTipTimer = null;\n if (this.socket?.connected) {\n socket.emit(BridgeEvent.Refused);\n socket.disconnect();\n logMsg(\n 'refused new connection: server already connected by another client',\n );\n return;\n }\n\n // Clean up stale old socket if it exists but is no longer connected\n if (this.socket) {\n try {\n this.socket.disconnect();\n } catch (e) {\n logMsg(`failed to disconnect stale socket: ${e}`);\n }\n this.socket = null;\n }\n\n try {\n logMsg('one client connected');\n this.socket = socket;\n\n const clientVersion = socket.handshake.query.version;\n logMsg(\n `Bridge connected, cli-side version v${__VERSION__}, browser-side version v${clientVersion}`,\n );\n\n socket.on(BridgeEvent.CallResponse, (params: BridgeCallResponse) => {\n const id = params.id;\n const response = params.response;\n const error = params.error;\n\n this.triggerCallResponseCallback(id, error, response);\n });\n\n socket.on('disconnect', (reason: string) => {\n this.connectionLost = true;\n this.connectionLostReason = reason;\n this.socket = null;\n\n // flush all pending calls as error and clean up completed calls\n for (const id in this.calls) {\n const call = this.calls[id];\n\n if (!call.responseTime) {\n const errorMessage = this.connectionLostErrorMsg();\n this.triggerCallResponseCallback(\n id,\n new Error(errorMessage),\n null,\n );\n }\n }\n\n // Clean up completed calls to prevent memory leaks in long-running sessions\n for (const id in this.calls) {\n if (this.calls[id].responseTime) {\n delete this.calls[id];\n }\n }\n\n this.onDisconnect?.(reason);\n });\n\n setTimeout(() => {\n this.onConnect?.();\n\n const payload = {\n version: __VERSION__,\n } as BridgeConnectedEventPayload;\n socket.emit(BridgeEvent.Connected, payload);\n Promise.resolve().then(() => {\n for (const id in this.calls) {\n if (this.calls[id].callTime === 0) {\n this.emitCall(id);\n }\n }\n });\n }, 0);\n } catch (e) {\n logMsg(`failed to handle connection event: ${e}`);\n }\n });\n\n this.io.on('close', () => {\n this.close();\n });\n });\n }\n\n private connectionLostErrorMsg = () => {\n return `Connection lost, reason: ${this.connectionLostReason}`;\n };\n\n private async triggerCallResponseCallback(\n id: string | number,\n error: Error | string | null,\n response: any,\n ) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n // Ensure error is always an Error object (bridge client may send strings)\n if (error) {\n call.error =\n error instanceof Error\n ? error\n : new Error(typeof error === 'string' ? error : String(error));\n } else {\n call.error = undefined;\n }\n call.response = response;\n call.responseTime = Date.now();\n\n call.callback(call.error, response);\n }\n\n private async emitCall(id: string) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n\n if (this.connectionLost) {\n const message = `Connection lost, reason: ${this.connectionLostReason}`;\n call.callback(new Error(message), null);\n return;\n }\n\n if (this.socket) {\n this.socket.emit(BridgeEvent.Call, {\n id,\n method: call.method,\n args: call.args,\n });\n call.callTime = Date.now();\n }\n }\n\n async call<T = any>(\n method: string,\n args: any[],\n timeout = BridgeCallTimeout,\n ): Promise<T> {\n const id = `${this.callId++}`;\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n logMsg(`bridge call timeout, id=${id}, method=${method}, args=`, args);\n this.calls[id].error = new Error(\n `Bridge call timeout after ${timeout}ms: ${method}`,\n );\n reject(this.calls[id].error);\n }, timeout);\n\n this.calls[id] = {\n method,\n args,\n response: null,\n callTime: 0,\n responseTime: 0,\n callback: (error: Error | undefined, response: any) => {\n clearTimeout(timeoutId);\n if (error) {\n reject(error);\n } else {\n resolve(response);\n }\n },\n };\n\n this.emitCall(id);\n });\n }\n\n // do NOT restart after close\n async close() {\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n const closeProcess = this.io?.close();\n this.io = null;\n\n return closeProcess;\n }\n}\n"],"names":["debug","getDebug","isTrustedOrigin","origin","killRunningServer","port","host","client","ClientIO","DefaultBridgeServerPort","BridgeSignalKill","sleep","e","BridgeServer","opts","timeout","Promise","resolve","reject","Error","setTimeout","BridgeErrorCodeNoClientConnected","logMsg","httpServer","createServer","_req","res","err","Server","socket","next","url","console","clearTimeout","BridgeEvent","clientVersion","params","id","response","error","reason","call","errorMessage","payload","__VERSION__","String","undefined","Date","message","method","args","BridgeCallTimeout","timeoutId","closeProcess","onConnect","onDisconnect","closeConflictServer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bridge-mode/io-server.mjs","sources":["../../../src/bridge-mode/io-server.ts"],"sourcesContent":["import {\n type IncomingMessage,\n type ServerResponse,\n createServer,\n} from 'node:http';\nimport { sleep } from '@midscene/core/utils';\nimport { getDebug } from '@midscene/shared/logger';\nimport { logMsg } from '@midscene/shared/utils';\nimport { Server, type Socket as ServerSocket } from 'socket.io';\nimport { io as ClientIO } from 'socket.io-client';\n\nimport {\n type BridgeCall,\n type BridgeCallResponse,\n BridgeCallTimeout,\n type BridgeConnectedEventPayload,\n BridgeErrorCodeNoClientConnected,\n BridgeEvent,\n BridgeSignalKill,\n DefaultBridgeServerHost,\n DefaultBridgeServerPort,\n} from './common';\n\nconst debug = getDebug('web:bridge:io-server');\n\ndeclare const __VERSION__: string;\n\n/**\n * Returns true if the given Origin header value is allowed to connect\n * to the bridge server. Trusted origins are:\n * - No Origin header (local Node.js processes like killRunningServer)\n * - chrome-extension:// origins (the Midscene Chrome extension)\n *\n * Browser pages (https://evil.com, etc.) are rejected to prevent\n * Cross-Site WebSocket Hijacking (CSWSH) attacks.\n */\nfunction isTrustedOrigin(origin: string | undefined): boolean {\n if (!origin) return true;\n if (origin.startsWith('chrome-extension://')) return true;\n return false;\n}\n\nexport const killRunningServer = async (\n port?: number,\n host = DefaultBridgeServerHost,\n) => {\n try {\n const client = ClientIO(`ws://${host}:${port || DefaultBridgeServerPort}`, {\n query: {\n [BridgeSignalKill]: 1,\n },\n });\n await sleep(300);\n await client.close();\n } catch (e) {\n debug('failed to kill port: %O', e);\n }\n};\n\n// ws server, this is where the request is sent\nexport class BridgeServer {\n private callId = 0;\n private io: Server | null = null;\n private socket: ServerSocket | null = null;\n private listeningTimeoutId: NodeJS.Timeout | null = null;\n private listeningTimerFlag = false;\n private connectionTipTimer: NodeJS.Timeout | null = null;\n public calls: Record<string, BridgeCall> = {};\n\n private connectionLost = false;\n private connectionLostReason = '';\n\n constructor(\n public host: string,\n public port: number,\n public onConnect?: () => void,\n public onDisconnect?: (reason: string) => void,\n public closeConflictServer?: boolean,\n ) {}\n\n async listen(\n opts: {\n timeout?: number | false;\n } = {},\n ): Promise<void> {\n const { timeout = 30000 } = opts;\n\n if (this.closeConflictServer) {\n await killRunningServer(this.port, this.host);\n }\n\n return new Promise((resolve, reject) => {\n if (this.listeningTimerFlag) {\n return reject(new Error('already listening'));\n }\n this.listeningTimerFlag = true;\n\n this.listeningTimeoutId = timeout\n ? setTimeout(() => {\n reject(\n new Error(\n `no extension connected after ${timeout}ms (${BridgeErrorCodeNoClientConnected})`,\n ),\n );\n }, timeout)\n : null;\n\n this.connectionTipTimer =\n !timeout || timeout > 3000\n ? setTimeout(() => {\n logMsg('waiting for bridge to connect...');\n }, 2000)\n : null;\n\n // Create HTTP server — no /bridge-auth endpoint needed since we use\n // pure Origin checking in the Socket.IO middleware below.\n const httpServer = createServer(\n (_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(404);\n res.end();\n },\n );\n\n // Set up HTTP server event listeners FIRST\n httpServer.once('listening', () => {\n resolve();\n });\n\n httpServer.once('error', (err: Error) => {\n reject(new Error(`Bridge Listening Error: ${err.message}`));\n });\n\n // Always honor the configured interface, including the loopback default.\n httpServer.listen(this.port, this.host);\n\n // Now create Socket.IO Server attached to the already-listening HTTP server\n this.io = new Server(httpServer, {\n maxHttpBufferSize: 100 * 1024 * 1024, // 100MB\n // Increase pingTimeout to tolerate Chrome MV3 Service Worker suspension.\n // The SW keepalive alarm fires every ~24s; default pingTimeout (20s) may\n // be too short if the SW is suspended between alarm pings.\n pingTimeout: 60000,\n });\n\n this.io.use((socket, next) => {\n // Reject connections from untrusted browser origins to prevent\n // Cross-Site WebSocket Hijacking (CSWSH) and unauthorized kill signals.\n // The browser automatically sets the Origin header on WebSocket\n // handshakes, and JavaScript cannot override it.\n const origin = socket.handshake.headers.origin;\n if (!isTrustedOrigin(origin)) {\n debug('connection rejected: untrusted origin=%s', origin);\n return next(new Error('origin not allowed'));\n }\n\n // Allow new connections to replace old ones (reconnection after\n // extension Stop→Start). If the old socket is already disconnected\n // or unresponsive, accept the new connection immediately.\n if (\n socket.handshake.url.includes(BridgeSignalKill) === false &&\n this.socket?.connected\n ) {\n return next(new Error('server already connected by another client'));\n }\n next();\n });\n\n this.io.on('connection', (socket) => {\n // check the connection url\n const url = socket.handshake.url;\n if (url.includes(BridgeSignalKill)) {\n console.warn('kill signal received, closing bridge server');\n return this.close();\n }\n\n this.connectionLost = false;\n this.connectionLostReason = '';\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.listeningTimeoutId = null;\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n this.connectionTipTimer = null;\n if (this.socket?.connected) {\n socket.emit(BridgeEvent.Refused);\n socket.disconnect();\n logMsg(\n 'refused new connection: server already connected by another client',\n );\n return;\n }\n\n // Clean up stale old socket if it exists but is no longer connected\n if (this.socket) {\n try {\n this.socket.disconnect();\n } catch (e) {\n logMsg(`failed to disconnect stale socket: ${e}`);\n }\n this.socket = null;\n }\n\n try {\n logMsg('one client connected');\n this.socket = socket;\n\n const clientVersion = socket.handshake.query.version;\n logMsg(\n `Bridge connected, cli-side version v${__VERSION__}, browser-side version v${clientVersion}`,\n );\n\n socket.on(BridgeEvent.CallResponse, (params: BridgeCallResponse) => {\n const id = params.id;\n const response = params.response;\n const error = params.error;\n\n this.triggerCallResponseCallback(id, error, response);\n });\n\n socket.on('disconnect', (reason: string) => {\n this.connectionLost = true;\n this.connectionLostReason = reason;\n this.socket = null;\n\n // flush all pending calls as error and clean up completed calls\n for (const id in this.calls) {\n const call = this.calls[id];\n\n if (!call.responseTime) {\n const errorMessage = this.connectionLostErrorMsg();\n this.triggerCallResponseCallback(\n id,\n new Error(errorMessage),\n null,\n );\n }\n }\n\n // Clean up completed calls to prevent memory leaks in long-running sessions\n for (const id in this.calls) {\n if (this.calls[id].responseTime) {\n delete this.calls[id];\n }\n }\n\n this.onDisconnect?.(reason);\n });\n\n setTimeout(() => {\n this.onConnect?.();\n\n const payload = {\n version: __VERSION__,\n } as BridgeConnectedEventPayload;\n socket.emit(BridgeEvent.Connected, payload);\n Promise.resolve().then(() => {\n for (const id in this.calls) {\n if (this.calls[id].callTime === 0) {\n this.emitCall(id);\n }\n }\n });\n }, 0);\n } catch (e) {\n logMsg(`failed to handle connection event: ${e}`);\n }\n });\n\n this.io.on('close', () => {\n this.close();\n });\n });\n }\n\n private connectionLostErrorMsg = () => {\n return `Connection lost, reason: ${this.connectionLostReason}`;\n };\n\n private async triggerCallResponseCallback(\n id: string | number,\n error: Error | string | null,\n response: any,\n ) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n // Ensure error is always an Error object (bridge client may send strings)\n if (error) {\n call.error =\n error instanceof Error\n ? error\n : new Error(typeof error === 'string' ? error : String(error));\n } else {\n call.error = undefined;\n }\n call.response = response;\n call.responseTime = Date.now();\n\n call.callback(call.error, response);\n }\n\n private async emitCall(id: string) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n\n if (this.connectionLost) {\n const message = `Connection lost, reason: ${this.connectionLostReason}`;\n call.callback(new Error(message), null);\n return;\n }\n\n if (this.socket) {\n this.socket.emit(BridgeEvent.Call, {\n id,\n method: call.method,\n args: call.args,\n });\n call.callTime = Date.now();\n }\n }\n\n async call<T = any>(\n method: string,\n args: any[],\n timeout = BridgeCallTimeout,\n ): Promise<T> {\n const id = `${this.callId++}`;\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n logMsg(`bridge call timeout, id=${id}, method=${method}, args=`, args);\n this.calls[id].error = new Error(\n `Bridge call timeout after ${timeout}ms: ${method}`,\n );\n reject(this.calls[id].error);\n }, timeout);\n\n this.calls[id] = {\n method,\n args,\n response: null,\n callTime: 0,\n responseTime: 0,\n callback: (error: Error | undefined, response: any) => {\n clearTimeout(timeoutId);\n if (error) {\n reject(error);\n } else {\n resolve(response);\n }\n },\n };\n\n this.emitCall(id);\n });\n }\n\n // do NOT restart after close\n async close() {\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n const closeProcess = this.io?.close();\n this.io = null;\n\n return closeProcess;\n }\n}\n"],"names":["debug","getDebug","isTrustedOrigin","origin","killRunningServer","port","host","DefaultBridgeServerHost","client","ClientIO","DefaultBridgeServerPort","BridgeSignalKill","sleep","e","BridgeServer","opts","timeout","Promise","resolve","reject","Error","setTimeout","BridgeErrorCodeNoClientConnected","logMsg","httpServer","createServer","_req","res","err","Server","socket","next","url","console","clearTimeout","BridgeEvent","clientVersion","params","id","response","error","reason","call","errorMessage","payload","__VERSION__","String","undefined","Date","message","method","args","BridgeCallTimeout","timeoutId","closeProcess","onConnect","onDisconnect","closeConflictServer"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from "@midscene/shared/utils";
|
|
2
2
|
import page from "../chrome-extension/page.mjs";
|
|
3
|
-
import { BridgeEvent, DefaultBridgeServerPort, KeyboardEvent, MouseEvent } from "./common.mjs";
|
|
3
|
+
import { BridgeEvent, DefaultBridgeServerHost, DefaultBridgeServerPort, KeyboardEvent, MouseEvent } from "./common.mjs";
|
|
4
4
|
import { BridgeClient } from "./io-client.mjs";
|
|
5
5
|
function _define_property(obj, key, value) {
|
|
6
6
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -49,7 +49,7 @@ function waitForTabNavigationComplete(tabId, targetUrl, timeoutMs = NEW_TAB_LOAD
|
|
|
49
49
|
}
|
|
50
50
|
class ExtensionBridgePageBrowserSide extends page {
|
|
51
51
|
async setupBridgeClient() {
|
|
52
|
-
const endpoint = this.serverEndpoint || `ws
|
|
52
|
+
const endpoint = this.serverEndpoint || `ws://${DefaultBridgeServerHost}:${DefaultBridgeServerPort}`;
|
|
53
53
|
let resolveConfirmationGate = ()=>{};
|
|
54
54
|
if (this.onConnectionRequest) this.confirmationPromise = new Promise((resolve)=>{
|
|
55
55
|
resolveConfirmationGate = resolve;
|
|
@@ -100,7 +100,7 @@ class ExtensionBridgePageBrowserSide extends page {
|
|
|
100
100
|
throw new Error('Connection denied by user');
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.12.
|
|
103
|
+
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.12.5-beta-20260907092148.0`, 'log');
|
|
104
104
|
}
|
|
105
105
|
async connect() {
|
|
106
106
|
return await this.setupBridgeClient();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-mode/page-browser-side.mjs","sources":["../../../src/bridge-mode/page-browser-side.ts"],"sourcesContent":["import { assert } from '@midscene/shared/utils';\nimport ChromeExtensionProxyPage from '../chrome-extension/page';\nimport type {\n ChromePageDestroyOptions,\n KeyboardAction,\n MouseAction,\n} from '../web-page';\nimport {\n type BridgeConnectTabOptions,\n BridgeEvent,\n DefaultBridgeServerPort,\n KeyboardEvent,\n MouseEvent,\n} from './common';\nimport { BridgeClient } from './io-client';\n\ndeclare const __VERSION__: string;\n\nconst NEW_TAB_LOAD_TIMEOUT_MS = 30_000;\n\nfunction isBlankUrl(url: string | undefined): boolean {\n if (!url) return true;\n return url === 'about:blank' || url.startsWith('chrome://newtab');\n}\n\n// Wait until the freshly created tab has navigated away from about:blank\n// and reached `status === 'complete'`. Resolves on timeout instead of\n// throwing so callers degrade to the existing lazy-attach behavior.\nfunction waitForTabNavigationComplete(\n tabId: number,\n targetUrl: string,\n timeoutMs = NEW_TAB_LOAD_TIMEOUT_MS,\n): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n try {\n chrome.tabs.onUpdated.removeListener(onUpdated);\n } catch {}\n clearTimeout(timer);\n resolve();\n };\n\n const isReady = (tab: chrome.tabs.Tab | undefined): boolean => {\n if (!tab) return false;\n if (tab.status !== 'complete') return false;\n const currentUrl = tab.url || tab.pendingUrl || '';\n // Skip the initial about:blank \"complete\" that fires before\n // the target URL navigation kicks in.\n if (isBlankUrl(currentUrl) && !isBlankUrl(targetUrl)) return false;\n return true;\n };\n\n const onUpdated = (\n id: number,\n _info: chrome.tabs.TabChangeInfo,\n tab: chrome.tabs.Tab,\n ) => {\n if (id !== tabId) return;\n if (isReady(tab)) finish();\n };\n\n chrome.tabs.onUpdated.addListener(onUpdated);\n const timer = setTimeout(finish, timeoutMs);\n\n // Handle the race where the tab already finished loading before\n // we registered the listener.\n chrome.tabs\n .get(tabId)\n .then((tab) => {\n if (isReady(tab)) finish();\n })\n .catch(() => {});\n });\n}\n\nexport class ExtensionBridgePageBrowserSide extends ChromeExtensionProxyPage {\n public bridgeClient: BridgeClient | null = null;\n\n private destroyOptions?: ChromePageDestroyOptions;\n\n private newlyCreatedTabIds: number[] = [];\n\n // Connection confirmation state\n private confirmationPromise: Promise<boolean> | null = null;\n\n constructor(\n public serverEndpoint?: string,\n public onDisconnect: () => void = () => {},\n public onLogMessage: (\n message: string,\n type: 'log' | 'status',\n ) => void = () => {},\n forceSameTabNavigation = true,\n public onConnectionRequest?: () => Promise<boolean>,\n ) {\n super(forceSameTabNavigation);\n }\n\n private async setupBridgeClient() {\n const endpoint =\n this.serverEndpoint || `ws://localhost:${DefaultBridgeServerPort}`;\n\n // Create confirmation gate BEFORE establishing connection,\n // so that any calls received immediately after connection are blocked\n // until user confirms. This prevents a race condition where server-side\n // queued calls bypass the confirmation dialog.\n let resolveConfirmationGate: (allowed: boolean) => void = () => {};\n if (this.onConnectionRequest) {\n this.confirmationPromise = new Promise<boolean>((resolve) => {\n resolveConfirmationGate = resolve;\n });\n }\n\n this.bridgeClient = new BridgeClient(\n endpoint,\n async (method, args: any[]) => {\n // Wait for user confirmation before processing any commands\n if (this.confirmationPromise) {\n const allowed = await this.confirmationPromise;\n if (!allowed) {\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(`bridge call from cli side: ${method}`, 'log');\n if (method === BridgeEvent.ConnectNewTabWithUrl) {\n return this.connectNewTabWithUrl.apply(\n this,\n args as unknown as [string],\n );\n }\n\n if (method === BridgeEvent.GetBrowserTabList) {\n return this.getBrowserTabList.apply(this, args as any);\n }\n\n if (method === BridgeEvent.SetActiveTabId) {\n return this.setActiveTabId.apply(this, args as any);\n }\n\n if (method === BridgeEvent.ConnectCurrentTab) {\n return this.connectCurrentTab.apply(this, args as any);\n }\n\n if (method === BridgeEvent.UpdateAgentStatus) {\n return this.onLogMessage(args[0] as string, 'status');\n }\n\n const tabId = await this.getActiveTabId();\n if (!tabId || tabId === 0) {\n throw new Error('no tab is connected');\n }\n\n // this.onLogMessage(`calling method: ${method}`);\n\n if (method.startsWith(MouseEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof MouseAction;\n if (actionName === 'drag') {\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n\n if (method.startsWith(KeyboardEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof KeyboardAction;\n if (actionName === 'press') {\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n\n // Generic method lookup. Methods like `setWaterFlowAnimationEnabled`\n // and `getWaterFlowAnimationEnabled` are inherited from\n // ChromeExtensionProxyPage and found via `this[method]`.\n if (!this[method as keyof ChromeExtensionProxyPage]) {\n this.onLogMessage(`method not found: ${method}`, 'log');\n return undefined;\n }\n\n try {\n // @ts-expect-error\n const result = await this[method as keyof ChromeExtensionProxyPage](\n ...args,\n );\n return result;\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Unknown error';\n this.onLogMessage(\n `Error calling method: ${method}, ${errorMessage}`,\n 'log',\n );\n throw new Error(errorMessage, { cause: e });\n }\n },\n // on disconnect\n () => {\n return this.destroy();\n },\n );\n await this.bridgeClient.connect();\n\n // Show confirmation dialog after connection is established\n if (this.onConnectionRequest) {\n this.onLogMessage('Waiting for user confirmation...', 'log');\n const allowed = await this.onConnectionRequest();\n resolveConfirmationGate(allowed);\n this.confirmationPromise = null;\n\n if (!allowed) {\n this.onLogMessage('Connection denied by user', 'log');\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(\n `Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v${__VERSION__}`,\n 'log',\n );\n }\n\n public async connect() {\n return await this.setupBridgeClient();\n }\n\n public async connectNewTabWithUrl(\n url: string,\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n // Otherwise the animation gets injected during the connection flow\n // before the CLI side's setWaterFlowAnimationEnabled(false) arrives.\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n // Preserve the historical default: activate the new tab unless the caller opts into background mode.\n const activate = options.activateTab ?? true;\n const tab = await chrome.tabs.create({ url, active: activate });\n const tabId = tab.id;\n assert(tabId, 'failed to get tabId after creating a new tab');\n\n // new tab\n this.onLogMessage(`Creating new tab: ${url}`, 'log');\n this.newlyCreatedTabIds.push(tabId);\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n // chrome.tabs.create returns immediately with an about:blank target,\n // then navigates to `url`. If we attach the debugger during that\n // cross-origin transition Site Isolation will detach it again, leaving\n // the first CDP command to fail with \"Debugger is not attached to the\n // tab\". Wait for navigation to settle so the lazy attach lands on a\n // stable target.\n await waitForTabNavigationComplete(tabId, url);\n\n await this.setActiveTabId(tabId, { activate });\n }\n\n public async connectCurrentTab(\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n const tabs = await chrome.tabs.query({ active: true, currentWindow: true });\n const tabId = tabs[0]?.id;\n assert(tabId, 'failed to get tabId');\n\n this.onLogMessage(`Connected to current tab: ${tabs[0]?.url}`, 'log');\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n await this.setActiveTabId(tabId);\n }\n\n public async setDestroyOptions(options: ChromePageDestroyOptions) {\n this.destroyOptions = options;\n }\n\n async destroy() {\n if (this.destroyOptions?.closeTab && this.newlyCreatedTabIds.length > 0) {\n this.onLogMessage('Closing all newly created tabs by bridge...', 'log');\n for (const tabId of this.newlyCreatedTabIds) {\n await chrome.tabs.remove(tabId);\n }\n this.newlyCreatedTabIds = [];\n }\n\n await super.destroy();\n\n if (this.bridgeClient) {\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n this.onDisconnect();\n }\n }\n}\n"],"names":["NEW_TAB_LOAD_TIMEOUT_MS","isBlankUrl","url","waitForTabNavigationComplete","tabId","targetUrl","timeoutMs","Promise","resolve","settled","finish","chrome","onUpdated","clearTimeout","timer","isReady","tab","currentUrl","id","_info","setTimeout","ExtensionBridgePageBrowserSide","ChromeExtensionProxyPage","endpoint","DefaultBridgeServerPort","resolveConfirmationGate","BridgeClient","method","args","allowed","Error","BridgeEvent","MouseEvent","actionName","KeyboardEvent","result","e","errorMessage","options","undefined","activate","assert","tabs","serverEndpoint","onDisconnect","onLogMessage","forceSameTabNavigation","onConnectionRequest"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bridge-mode/page-browser-side.mjs","sources":["../../../src/bridge-mode/page-browser-side.ts"],"sourcesContent":["import { assert } from '@midscene/shared/utils';\nimport ChromeExtensionProxyPage from '../chrome-extension/page';\nimport type {\n ChromePageDestroyOptions,\n KeyboardAction,\n MouseAction,\n} from '../web-page';\nimport {\n type BridgeConnectTabOptions,\n BridgeEvent,\n DefaultBridgeServerHost,\n DefaultBridgeServerPort,\n KeyboardEvent,\n MouseEvent,\n} from './common';\nimport { BridgeClient } from './io-client';\n\ndeclare const __VERSION__: string;\n\nconst NEW_TAB_LOAD_TIMEOUT_MS = 30_000;\n\nfunction isBlankUrl(url: string | undefined): boolean {\n if (!url) return true;\n return url === 'about:blank' || url.startsWith('chrome://newtab');\n}\n\n// Wait until the freshly created tab has navigated away from about:blank\n// and reached `status === 'complete'`. Resolves on timeout instead of\n// throwing so callers degrade to the existing lazy-attach behavior.\nfunction waitForTabNavigationComplete(\n tabId: number,\n targetUrl: string,\n timeoutMs = NEW_TAB_LOAD_TIMEOUT_MS,\n): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n try {\n chrome.tabs.onUpdated.removeListener(onUpdated);\n } catch {}\n clearTimeout(timer);\n resolve();\n };\n\n const isReady = (tab: chrome.tabs.Tab | undefined): boolean => {\n if (!tab) return false;\n if (tab.status !== 'complete') return false;\n const currentUrl = tab.url || tab.pendingUrl || '';\n // Skip the initial about:blank \"complete\" that fires before\n // the target URL navigation kicks in.\n if (isBlankUrl(currentUrl) && !isBlankUrl(targetUrl)) return false;\n return true;\n };\n\n const onUpdated = (\n id: number,\n _info: chrome.tabs.TabChangeInfo,\n tab: chrome.tabs.Tab,\n ) => {\n if (id !== tabId) return;\n if (isReady(tab)) finish();\n };\n\n chrome.tabs.onUpdated.addListener(onUpdated);\n const timer = setTimeout(finish, timeoutMs);\n\n // Handle the race where the tab already finished loading before\n // we registered the listener.\n chrome.tabs\n .get(tabId)\n .then((tab) => {\n if (isReady(tab)) finish();\n })\n .catch(() => {});\n });\n}\n\nexport class ExtensionBridgePageBrowserSide extends ChromeExtensionProxyPage {\n public bridgeClient: BridgeClient | null = null;\n\n private destroyOptions?: ChromePageDestroyOptions;\n\n private newlyCreatedTabIds: number[] = [];\n\n // Connection confirmation state\n private confirmationPromise: Promise<boolean> | null = null;\n\n constructor(\n public serverEndpoint?: string,\n public onDisconnect: () => void = () => {},\n public onLogMessage: (\n message: string,\n type: 'log' | 'status',\n ) => void = () => {},\n forceSameTabNavigation = true,\n public onConnectionRequest?: () => Promise<boolean>,\n ) {\n super(forceSameTabNavigation);\n }\n\n private async setupBridgeClient() {\n const endpoint =\n this.serverEndpoint ||\n `ws://${DefaultBridgeServerHost}:${DefaultBridgeServerPort}`;\n\n // Create confirmation gate BEFORE establishing connection,\n // so that any calls received immediately after connection are blocked\n // until user confirms. This prevents a race condition where server-side\n // queued calls bypass the confirmation dialog.\n let resolveConfirmationGate: (allowed: boolean) => void = () => {};\n if (this.onConnectionRequest) {\n this.confirmationPromise = new Promise<boolean>((resolve) => {\n resolveConfirmationGate = resolve;\n });\n }\n\n this.bridgeClient = new BridgeClient(\n endpoint,\n async (method, args: any[]) => {\n // Wait for user confirmation before processing any commands\n if (this.confirmationPromise) {\n const allowed = await this.confirmationPromise;\n if (!allowed) {\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(`bridge call from cli side: ${method}`, 'log');\n if (method === BridgeEvent.ConnectNewTabWithUrl) {\n return this.connectNewTabWithUrl.apply(\n this,\n args as unknown as [string],\n );\n }\n\n if (method === BridgeEvent.GetBrowserTabList) {\n return this.getBrowserTabList.apply(this, args as any);\n }\n\n if (method === BridgeEvent.SetActiveTabId) {\n return this.setActiveTabId.apply(this, args as any);\n }\n\n if (method === BridgeEvent.ConnectCurrentTab) {\n return this.connectCurrentTab.apply(this, args as any);\n }\n\n if (method === BridgeEvent.UpdateAgentStatus) {\n return this.onLogMessage(args[0] as string, 'status');\n }\n\n const tabId = await this.getActiveTabId();\n if (!tabId || tabId === 0) {\n throw new Error('no tab is connected');\n }\n\n // this.onLogMessage(`calling method: ${method}`);\n\n if (method.startsWith(MouseEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof MouseAction;\n if (actionName === 'drag') {\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n\n if (method.startsWith(KeyboardEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof KeyboardAction;\n if (actionName === 'press') {\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n\n // Generic method lookup. Methods like `setWaterFlowAnimationEnabled`\n // and `getWaterFlowAnimationEnabled` are inherited from\n // ChromeExtensionProxyPage and found via `this[method]`.\n if (!this[method as keyof ChromeExtensionProxyPage]) {\n this.onLogMessage(`method not found: ${method}`, 'log');\n return undefined;\n }\n\n try {\n // @ts-expect-error\n const result = await this[method as keyof ChromeExtensionProxyPage](\n ...args,\n );\n return result;\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Unknown error';\n this.onLogMessage(\n `Error calling method: ${method}, ${errorMessage}`,\n 'log',\n );\n throw new Error(errorMessage, { cause: e });\n }\n },\n // on disconnect\n () => {\n return this.destroy();\n },\n );\n await this.bridgeClient.connect();\n\n // Show confirmation dialog after connection is established\n if (this.onConnectionRequest) {\n this.onLogMessage('Waiting for user confirmation...', 'log');\n const allowed = await this.onConnectionRequest();\n resolveConfirmationGate(allowed);\n this.confirmationPromise = null;\n\n if (!allowed) {\n this.onLogMessage('Connection denied by user', 'log');\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(\n `Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v${__VERSION__}`,\n 'log',\n );\n }\n\n public async connect() {\n return await this.setupBridgeClient();\n }\n\n public async connectNewTabWithUrl(\n url: string,\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n // Otherwise the animation gets injected during the connection flow\n // before the CLI side's setWaterFlowAnimationEnabled(false) arrives.\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n // Preserve the historical default: activate the new tab unless the caller opts into background mode.\n const activate = options.activateTab ?? true;\n const tab = await chrome.tabs.create({ url, active: activate });\n const tabId = tab.id;\n assert(tabId, 'failed to get tabId after creating a new tab');\n\n // new tab\n this.onLogMessage(`Creating new tab: ${url}`, 'log');\n this.newlyCreatedTabIds.push(tabId);\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n // chrome.tabs.create returns immediately with an about:blank target,\n // then navigates to `url`. If we attach the debugger during that\n // cross-origin transition Site Isolation will detach it again, leaving\n // the first CDP command to fail with \"Debugger is not attached to the\n // tab\". Wait for navigation to settle so the lazy attach lands on a\n // stable target.\n await waitForTabNavigationComplete(tabId, url);\n\n await this.setActiveTabId(tabId, { activate });\n }\n\n public async connectCurrentTab(\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n const tabs = await chrome.tabs.query({ active: true, currentWindow: true });\n const tabId = tabs[0]?.id;\n assert(tabId, 'failed to get tabId');\n\n this.onLogMessage(`Connected to current tab: ${tabs[0]?.url}`, 'log');\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n await this.setActiveTabId(tabId);\n }\n\n public async setDestroyOptions(options: ChromePageDestroyOptions) {\n this.destroyOptions = options;\n }\n\n async destroy() {\n if (this.destroyOptions?.closeTab && this.newlyCreatedTabIds.length > 0) {\n this.onLogMessage('Closing all newly created tabs by bridge...', 'log');\n for (const tabId of this.newlyCreatedTabIds) {\n await chrome.tabs.remove(tabId);\n }\n this.newlyCreatedTabIds = [];\n }\n\n await super.destroy();\n\n if (this.bridgeClient) {\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n this.onDisconnect();\n }\n }\n}\n"],"names":["NEW_TAB_LOAD_TIMEOUT_MS","isBlankUrl","url","waitForTabNavigationComplete","tabId","targetUrl","timeoutMs","Promise","resolve","settled","finish","chrome","onUpdated","clearTimeout","timer","isReady","tab","currentUrl","id","_info","setTimeout","ExtensionBridgePageBrowserSide","ChromeExtensionProxyPage","endpoint","DefaultBridgeServerHost","DefaultBridgeServerPort","resolveConfirmationGate","BridgeClient","method","args","allowed","Error","BridgeEvent","MouseEvent","actionName","KeyboardEvent","result","e","errorMessage","options","undefined","activate","assert","tabs","serverEndpoint","onDisconnect","onLogMessage","forceSameTabNavigation","onConnectionRequest"],"mappings":"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"}
|
package/dist/es/cli.mjs
CHANGED
|
@@ -18,7 +18,7 @@ Promise.resolve().then(()=>{
|
|
|
18
18
|
return runToolsCLI(tools, 'midscene-web', {
|
|
19
19
|
stripPrefix: 'web_',
|
|
20
20
|
argv: parsedOptions.argv,
|
|
21
|
-
version: "1.12.
|
|
21
|
+
version: "1.12.5-beta-20260907092148.0",
|
|
22
22
|
extraCommands: createReportCliCommands()
|
|
23
23
|
});
|
|
24
24
|
}).catch((e)=>{
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { overrideAIConfig } from "@midscene/shared/env";
|
|
2
|
+
import { WebPage } from "./page.mjs";
|
|
3
|
+
import { PlaywrightAgent, PlaywrightPageAgent } from "./page-agent.mjs";
|
|
4
|
+
import { PlaywrightBrowserAgent } from "./browser-agent.mjs";
|
|
5
|
+
export { PlaywrightAgent, PlaywrightBrowserAgent, PlaywrightPageAgent, WebPage as PlaywrightWebPage, overrideAIConfig };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { resolveBrowserAgentRuntimeOptions } from "../common/browser-agent.mjs";
|
|
2
|
-
import { PlaywrightAgent, PlaywrightBrowserAgent } from "./index.mjs";
|
|
3
2
|
import { processCacheConfig } from "@midscene/core/utils";
|
|
4
3
|
import { DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from "@midscene/shared/constants";
|
|
5
4
|
import { getDebug } from "@midscene/shared/logger";
|
|
6
5
|
import { replaceIllegalPathCharsAndSpace, uuid } from "@midscene/shared/utils";
|
|
7
6
|
import { test } from "@playwright/test";
|
|
7
|
+
import { PlaywrightAgent, PlaywrightBrowserAgent } from "./agent.mjs";
|
|
8
8
|
import { buildPlaywrightReportTag } from "./report-filename.mjs";
|
|
9
9
|
const debugPage = getDebug('web:playwright:ai-fixture');
|
|
10
10
|
const groupAndCaseForTest = (testInfo)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/ai-fixture.mjs","sources":["../../../src/playwright/ai-fixture.ts"],"sourcesContent":["import { resolveBrowserAgentRuntimeOptions } from '@/common/browser-agent';\nimport {\n PlaywrightAgent,\n PlaywrightBrowserAgent,\n type PlaywrightWebPage,\n} from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { replaceIllegalPathCharsAndSpace, uuid } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nimport { buildPlaywrightReportTag } from './report-filename';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\ntype AgentRecord = {\n agent: PageAgent<PlaywrightWebPage>;\n finalizePromise?: Promise<string | undefined>;\n finalReportPath?: string;\n};\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport type PlaywrightAiFixtureOptions = Omit<\n WebPageAgentOpt,\n | 'testId'\n | 'cacheId'\n | 'groupName'\n | 'groupDescription'\n | 'reportFileName'\n | 'cache'\n> & {\n autoFollowNewPage?: boolean;\n cache?: PlaywrightCache;\n};\n\nexport const PlaywrightAiFixture = (options?: PlaywrightAiFixtureOptions) => {\n const {\n forceSameTabNavigation = true,\n autoFollowNewPage = false,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n ...sharedAgentOptions\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const testAgentRecords = new Map<string, Map<string, AgentRecord>>();\n\n const getAgentRecordsForTest = (testInfo: TestInfo) => {\n let records = testAgentRecords.get(testInfo.testId);\n if (!records) {\n records = new Map<string, AgentRecord>();\n testAgentRecords.set(testInfo.testId, records);\n }\n return records;\n };\n\n const setReportAnnotation = (testInfo: TestInfo, reportPaths: string[]) => {\n testInfo.annotations = testInfo.annotations.filter((item) => {\n return item.type !== midsceneDumpAnnotationId;\n });\n\n for (const reportPath of reportPaths) {\n testInfo.annotations.push({\n type: midsceneDumpAnnotationId,\n description: reportPath,\n });\n }\n };\n\n const finalizeAgentRecord = async (\n record: AgentRecord,\n ): Promise<string | undefined> => {\n if (!record.finalizePromise) {\n record.finalizePromise = (async () => {\n await record.agent.destroy();\n const reportPath = record.agent.reportFile || undefined;\n record.finalReportPath = reportPath;\n return reportPath;\n })();\n }\n\n return await record.finalizePromise;\n };\n\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n // `replaceIllegalPathCharsAndSpace` intentionally preserves `/` and `\\`\n // so groupName/groupDescription can still carry hierarchy. But\n // ReportGenerator rejects path separators in the file name, so the\n // report tag builder strips them without changing the group metadata.\n const reportTag = buildPlaywrightReportTag(title, idForPage);\n\n if (autoFollowNewPage && forceSameTabNavigation === true) {\n throw new Error(\n '[midscene] autoFollowNewPage cannot be used with forceSameTabNavigation: true.',\n );\n }\n\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightAiFixture',\n pageScope: autoFollowNewPage ? 'browser' : 'page',\n forceSameTabNavigation: autoFollowNewPage\n ? undefined\n : forceSameTabNavigation,\n autoFollowNewPage,\n });\n\n const commonAgentOpts = {\n testId: reportTag,\n reportFileName: reportTag,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: true,\n ...sharedAgentOptions,\n ...opts,\n };\n\n const agent = autoFollowNewPage\n ? new PlaywrightBrowserAgent(page.context(), page, {\n ...commonAgentOpts,\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n })\n : new PlaywrightAgent(page, {\n ...commonAgentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n pageAgentMap[idForPage] = agent;\n const records = getAgentRecordsForTest(testInfo);\n const record: AgentRecord = { agent };\n records.set(idForPage, record);\n\n page.on('close', async () => {\n debugPage('page closed');\n try {\n await finalizeAgentRecord(record);\n } finally {\n delete pageAgentMap[idForPage];\n }\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiAction'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: unknown, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n type AgentMethod = (...methodArgs: any[]) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod).bind(\n agent,\n )(taskPrompt, ...args);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n return {\n _midsceneFinalizeReports: [\n // biome-ignore lint/correctness/noEmptyPattern: Playwright fixture callbacks must use object destructuring for the first parameter even when no fixtures are consumed.\n async ({}: Record<string, unknown>, use: any, testInfo: TestInfo) => {\n await use();\n\n const records = testAgentRecords.get(testInfo.testId);\n if (!records || records.size === 0) {\n return;\n }\n\n const reportPaths = (\n await Promise.all(\n Array.from(records.values()).map((record) =>\n finalizeAgentRecord(record),\n ),\n )\n ).filter((reportPath): reportPath is string => Boolean(reportPath));\n\n if (reportPaths.length > 0) {\n setReportAnnotation(testInfo, reportPaths);\n }\n\n testAgentRecords.delete(testInfo.testId);\n },\n { auto: true },\n ],\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: WebPageAgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n /**\n * @deprecated Use {@link PlaywrightAiFixture.aiAct} instead.\n */\n aiAction: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAction',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: OriginPlaywrightPage,\n opts?: WebPageAgentOpt,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(...args: Parameters<PageAgent['ai']>) => Promise<T>;\n aiAct: (\n ...args: Parameters<PageAgent['aiAct']>\n ) => ReturnType<PageAgent['aiAct']>;\n /**\n * @deprecated Use {@link PlayWrightAiFixtureType.aiAct} instead.\n */\n aiAction: (\n ...args: Parameters<PageAgent['aiAction']>\n ) => ReturnType<PageAgent['aiAction']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","PlaywrightAiFixture","options","forceSameTabNavigation","autoFollowNewPage","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","sharedAgentOptions","processTestCacheConfig","id","processCacheConfig","pageAgentMap","testAgentRecords","Map","getAgentRecordsForTest","records","setReportAnnotation","reportPaths","item","reportPath","finalizeAgentRecord","record","undefined","createOrReuseAgentForPage","page","opts","idForPage","uuid","file","title","cacheConfig","reportTag","buildPlaywrightReportTag","Error","runtimeOptions","resolveBrowserAgentRuntimeOptions","commonAgentOpts","agent","PlaywrightBrowserAgent","PlaywrightAgent","generateAiFunction","use","aiActionType","taskPrompt","args","Promise","resolve","reject","test","JSON","result","error","Array","Boolean","propsPage","finalCacheConfig","userCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playwright/ai-fixture.mjs","sources":["../../../src/playwright/ai-fixture.ts"],"sourcesContent":["import { resolveBrowserAgentRuntimeOptions } from '@/common/browser-agent';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { replaceIllegalPathCharsAndSpace, uuid } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nimport {\n PlaywrightAgent,\n PlaywrightBrowserAgent,\n type PlaywrightWebPage,\n} from './agent';\nimport { buildPlaywrightReportTag } from './report-filename';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\ntype AgentRecord = {\n agent: PageAgent<PlaywrightWebPage>;\n finalizePromise?: Promise<string | undefined>;\n finalReportPath?: string;\n};\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport type PlaywrightAiFixtureOptions = Omit<\n WebPageAgentOpt,\n | 'testId'\n | 'cacheId'\n | 'groupName'\n | 'groupDescription'\n | 'reportFileName'\n | 'cache'\n> & {\n autoFollowNewPage?: boolean;\n cache?: PlaywrightCache;\n};\n\nexport const PlaywrightAiFixture = (options?: PlaywrightAiFixtureOptions) => {\n const {\n forceSameTabNavigation = true,\n autoFollowNewPage = false,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n ...sharedAgentOptions\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const testAgentRecords = new Map<string, Map<string, AgentRecord>>();\n\n const getAgentRecordsForTest = (testInfo: TestInfo) => {\n let records = testAgentRecords.get(testInfo.testId);\n if (!records) {\n records = new Map<string, AgentRecord>();\n testAgentRecords.set(testInfo.testId, records);\n }\n return records;\n };\n\n const setReportAnnotation = (testInfo: TestInfo, reportPaths: string[]) => {\n testInfo.annotations = testInfo.annotations.filter((item) => {\n return item.type !== midsceneDumpAnnotationId;\n });\n\n for (const reportPath of reportPaths) {\n testInfo.annotations.push({\n type: midsceneDumpAnnotationId,\n description: reportPath,\n });\n }\n };\n\n const finalizeAgentRecord = async (\n record: AgentRecord,\n ): Promise<string | undefined> => {\n if (!record.finalizePromise) {\n record.finalizePromise = (async () => {\n await record.agent.destroy();\n const reportPath = record.agent.reportFile || undefined;\n record.finalReportPath = reportPath;\n return reportPath;\n })();\n }\n\n return await record.finalizePromise;\n };\n\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n // `replaceIllegalPathCharsAndSpace` intentionally preserves `/` and `\\`\n // so groupName/groupDescription can still carry hierarchy. But\n // ReportGenerator rejects path separators in the file name, so the\n // report tag builder strips them without changing the group metadata.\n const reportTag = buildPlaywrightReportTag(title, idForPage);\n\n if (autoFollowNewPage && forceSameTabNavigation === true) {\n throw new Error(\n '[midscene] autoFollowNewPage cannot be used with forceSameTabNavigation: true.',\n );\n }\n\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightAiFixture',\n pageScope: autoFollowNewPage ? 'browser' : 'page',\n forceSameTabNavigation: autoFollowNewPage\n ? undefined\n : forceSameTabNavigation,\n autoFollowNewPage,\n });\n\n const commonAgentOpts = {\n testId: reportTag,\n reportFileName: reportTag,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: true,\n ...sharedAgentOptions,\n ...opts,\n };\n\n const agent = autoFollowNewPage\n ? new PlaywrightBrowserAgent(page.context(), page, {\n ...commonAgentOpts,\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n })\n : new PlaywrightAgent(page, {\n ...commonAgentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n pageAgentMap[idForPage] = agent;\n const records = getAgentRecordsForTest(testInfo);\n const record: AgentRecord = { agent };\n records.set(idForPage, record);\n\n page.on('close', async () => {\n debugPage('page closed');\n try {\n await finalizeAgentRecord(record);\n } finally {\n delete pageAgentMap[idForPage];\n }\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiAction'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: unknown, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n type AgentMethod = (...methodArgs: any[]) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod).bind(\n agent,\n )(taskPrompt, ...args);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n return {\n _midsceneFinalizeReports: [\n // biome-ignore lint/correctness/noEmptyPattern: Playwright fixture callbacks must use object destructuring for the first parameter even when no fixtures are consumed.\n async ({}: Record<string, unknown>, use: any, testInfo: TestInfo) => {\n await use();\n\n const records = testAgentRecords.get(testInfo.testId);\n if (!records || records.size === 0) {\n return;\n }\n\n const reportPaths = (\n await Promise.all(\n Array.from(records.values()).map((record) =>\n finalizeAgentRecord(record),\n ),\n )\n ).filter((reportPath): reportPath is string => Boolean(reportPath));\n\n if (reportPaths.length > 0) {\n setReportAnnotation(testInfo, reportPaths);\n }\n\n testAgentRecords.delete(testInfo.testId);\n },\n { auto: true },\n ],\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: WebPageAgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n /**\n * @deprecated Use {@link PlaywrightAiFixture.aiAct} instead.\n */\n aiAction: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAction',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: OriginPlaywrightPage,\n opts?: WebPageAgentOpt,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(...args: Parameters<PageAgent['ai']>) => Promise<T>;\n aiAct: (\n ...args: Parameters<PageAgent['aiAct']>\n ) => ReturnType<PageAgent['aiAct']>;\n /**\n * @deprecated Use {@link PlayWrightAiFixtureType.aiAct} instead.\n */\n aiAction: (\n ...args: Parameters<PageAgent['aiAction']>\n ) => ReturnType<PageAgent['aiAction']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","PlaywrightAiFixture","options","forceSameTabNavigation","autoFollowNewPage","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","sharedAgentOptions","processTestCacheConfig","id","processCacheConfig","pageAgentMap","testAgentRecords","Map","getAgentRecordsForTest","records","setReportAnnotation","reportPaths","item","reportPath","finalizeAgentRecord","record","undefined","createOrReuseAgentForPage","page","opts","idForPage","uuid","file","title","cacheConfig","reportTag","buildPlaywrightReportTag","Error","runtimeOptions","resolveBrowserAgentRuntimeOptions","commonAgentOpts","agent","PlaywrightBrowserAgent","PlaywrightAgent","generateAiFunction","use","aiActionType","taskPrompt","args","Promise","resolve","reject","test","JSON","result","error","Array","Boolean","propsPage","finalCacheConfig","userCache"],"mappings":"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"}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
import { PlaywrightAiFixture } from "./ai-fixture.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { PlaywrightAgent, PlaywrightPageAgent } from "./page-agent.mjs";
|
|
5
|
-
import { PlaywrightBrowserAgent } from "./browser-agent.mjs";
|
|
6
|
-
export { PlaywrightAgent, PlaywrightAiFixture, PlaywrightBrowserAgent, PlaywrightPageAgent, WebPage as PlaywrightWebPage, overrideAIConfig };
|
|
2
|
+
export * from "./agent.mjs";
|
|
3
|
+
export { PlaywrightAiFixture };
|
|
@@ -50,7 +50,7 @@ function isTrustedOrigin(origin) {
|
|
|
50
50
|
if (origin.startsWith('chrome-extension://')) return true;
|
|
51
51
|
return false;
|
|
52
52
|
}
|
|
53
|
-
const killRunningServer = async (port, host =
|
|
53
|
+
const killRunningServer = async (port, host = external_common_js_namespaceObject.DefaultBridgeServerHost)=>{
|
|
54
54
|
try {
|
|
55
55
|
const client = (0, external_socket_io_client_namespaceObject.io)(`ws://${host}:${port || external_common_js_namespaceObject.DefaultBridgeServerPort}`, {
|
|
56
56
|
query: {
|
|
@@ -86,8 +86,7 @@ class BridgeServer {
|
|
|
86
86
|
httpServer.once('error', (err)=>{
|
|
87
87
|
reject(new Error(`Bridge Listening Error: ${err.message}`));
|
|
88
88
|
});
|
|
89
|
-
|
|
90
|
-
else httpServer.listen(this.port, this.host);
|
|
89
|
+
httpServer.listen(this.port, this.host);
|
|
91
90
|
this.io = new external_socket_io_namespaceObject.Server(httpServer, {
|
|
92
91
|
maxHttpBufferSize: 104857600,
|
|
93
92
|
pingTimeout: 60000
|
|
@@ -131,7 +130,7 @@ class BridgeServer {
|
|
|
131
130
|
(0, shared_utils_namespaceObject.logMsg)('one client connected');
|
|
132
131
|
this.socket = socket;
|
|
133
132
|
const clientVersion = socket.handshake.query.version;
|
|
134
|
-
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.12.
|
|
133
|
+
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.12.5-beta-20260907092148.0, browser-side version v${clientVersion}`);
|
|
135
134
|
socket.on(external_common_js_namespaceObject.BridgeEvent.CallResponse, (params)=>{
|
|
136
135
|
const id = params.id;
|
|
137
136
|
const response = params.response;
|
|
@@ -155,7 +154,7 @@ class BridgeServer {
|
|
|
155
154
|
setTimeout(()=>{
|
|
156
155
|
this.onConnect?.();
|
|
157
156
|
const payload = {
|
|
158
|
-
version: "1.12.
|
|
157
|
+
version: "1.12.5-beta-20260907092148.0"
|
|
159
158
|
};
|
|
160
159
|
socket.emit(external_common_js_namespaceObject.BridgeEvent.Connected, payload);
|
|
161
160
|
Promise.resolve().then(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-mode/io-server.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/bridge-mode/io-server.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n type IncomingMessage,\n type ServerResponse,\n createServer,\n} from 'node:http';\nimport { sleep } from '@midscene/core/utils';\nimport { getDebug } from '@midscene/shared/logger';\nimport { logMsg } from '@midscene/shared/utils';\nimport { Server, type Socket as ServerSocket } from 'socket.io';\nimport { io as ClientIO } from 'socket.io-client';\n\nimport {\n type BridgeCall,\n type BridgeCallResponse,\n BridgeCallTimeout,\n type BridgeConnectedEventPayload,\n BridgeErrorCodeNoClientConnected,\n BridgeEvent,\n BridgeSignalKill,\n DefaultBridgeServerPort,\n} from './common';\n\nconst debug = getDebug('web:bridge:io-server');\n\ndeclare const __VERSION__: string;\n\n/**\n * Returns true if the given Origin header value is allowed to connect\n * to the bridge server. Trusted origins are:\n * - No Origin header (local Node.js processes like killRunningServer)\n * - chrome-extension:// origins (the Midscene Chrome extension)\n *\n * Browser pages (https://evil.com, etc.) are rejected to prevent\n * Cross-Site WebSocket Hijacking (CSWSH) attacks.\n */\nfunction isTrustedOrigin(origin: string | undefined): boolean {\n if (!origin) return true;\n if (origin.startsWith('chrome-extension://')) return true;\n return false;\n}\n\nexport const killRunningServer = async (port?: number, host = 'localhost') => {\n try {\n const client = ClientIO(`ws://${host}:${port || DefaultBridgeServerPort}`, {\n query: {\n [BridgeSignalKill]: 1,\n },\n });\n await sleep(300);\n await client.close();\n } catch (e) {\n debug('failed to kill port: %O', e);\n }\n};\n\n// ws server, this is where the request is sent\nexport class BridgeServer {\n private callId = 0;\n private io: Server | null = null;\n private socket: ServerSocket | null = null;\n private listeningTimeoutId: NodeJS.Timeout | null = null;\n private listeningTimerFlag = false;\n private connectionTipTimer: NodeJS.Timeout | null = null;\n public calls: Record<string, BridgeCall> = {};\n\n private connectionLost = false;\n private connectionLostReason = '';\n\n constructor(\n public host: string,\n public port: number,\n public onConnect?: () => void,\n public onDisconnect?: (reason: string) => void,\n public closeConflictServer?: boolean,\n ) {}\n\n async listen(\n opts: {\n timeout?: number | false;\n } = {},\n ): Promise<void> {\n const { timeout = 30000 } = opts;\n\n if (this.closeConflictServer) {\n await killRunningServer(this.port, this.host);\n }\n\n return new Promise((resolve, reject) => {\n if (this.listeningTimerFlag) {\n return reject(new Error('already listening'));\n }\n this.listeningTimerFlag = true;\n\n this.listeningTimeoutId = timeout\n ? setTimeout(() => {\n reject(\n new Error(\n `no extension connected after ${timeout}ms (${BridgeErrorCodeNoClientConnected})`,\n ),\n );\n }, timeout)\n : null;\n\n this.connectionTipTimer =\n !timeout || timeout > 3000\n ? setTimeout(() => {\n logMsg('waiting for bridge to connect...');\n }, 2000)\n : null;\n\n // Create HTTP server — no /bridge-auth endpoint needed since we use\n // pure Origin checking in the Socket.IO middleware below.\n const httpServer = createServer(\n (_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(404);\n res.end();\n },\n );\n\n // Set up HTTP server event listeners FIRST\n httpServer.once('listening', () => {\n resolve();\n });\n\n httpServer.once('error', (err: Error) => {\n reject(new Error(`Bridge Listening Error: ${err.message}`));\n });\n\n // Start listening BEFORE creating Socket.IO Server\n // When host is 127.0.0.1 (default), don't specify host to listen on all local interfaces (IPv4 + IPv6)\n // This ensures localhost resolves correctly in both IPv4 and IPv6 environments\n if (this.host === '127.0.0.1') {\n httpServer.listen(this.port);\n } else {\n httpServer.listen(this.port, this.host);\n }\n\n // Now create Socket.IO Server attached to the already-listening HTTP server\n this.io = new Server(httpServer, {\n maxHttpBufferSize: 100 * 1024 * 1024, // 100MB\n // Increase pingTimeout to tolerate Chrome MV3 Service Worker suspension.\n // The SW keepalive alarm fires every ~24s; default pingTimeout (20s) may\n // be too short if the SW is suspended between alarm pings.\n pingTimeout: 60000,\n });\n\n this.io.use((socket, next) => {\n // Reject connections from untrusted browser origins to prevent\n // Cross-Site WebSocket Hijacking (CSWSH) and unauthorized kill signals.\n // The browser automatically sets the Origin header on WebSocket\n // handshakes, and JavaScript cannot override it.\n const origin = socket.handshake.headers.origin;\n if (!isTrustedOrigin(origin)) {\n debug('connection rejected: untrusted origin=%s', origin);\n return next(new Error('origin not allowed'));\n }\n\n // Allow new connections to replace old ones (reconnection after\n // extension Stop→Start). If the old socket is already disconnected\n // or unresponsive, accept the new connection immediately.\n if (\n socket.handshake.url.includes(BridgeSignalKill) === false &&\n this.socket?.connected\n ) {\n return next(new Error('server already connected by another client'));\n }\n next();\n });\n\n this.io.on('connection', (socket) => {\n // check the connection url\n const url = socket.handshake.url;\n if (url.includes(BridgeSignalKill)) {\n console.warn('kill signal received, closing bridge server');\n return this.close();\n }\n\n this.connectionLost = false;\n this.connectionLostReason = '';\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.listeningTimeoutId = null;\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n this.connectionTipTimer = null;\n if (this.socket?.connected) {\n socket.emit(BridgeEvent.Refused);\n socket.disconnect();\n logMsg(\n 'refused new connection: server already connected by another client',\n );\n return;\n }\n\n // Clean up stale old socket if it exists but is no longer connected\n if (this.socket) {\n try {\n this.socket.disconnect();\n } catch (e) {\n logMsg(`failed to disconnect stale socket: ${e}`);\n }\n this.socket = null;\n }\n\n try {\n logMsg('one client connected');\n this.socket = socket;\n\n const clientVersion = socket.handshake.query.version;\n logMsg(\n `Bridge connected, cli-side version v${__VERSION__}, browser-side version v${clientVersion}`,\n );\n\n socket.on(BridgeEvent.CallResponse, (params: BridgeCallResponse) => {\n const id = params.id;\n const response = params.response;\n const error = params.error;\n\n this.triggerCallResponseCallback(id, error, response);\n });\n\n socket.on('disconnect', (reason: string) => {\n this.connectionLost = true;\n this.connectionLostReason = reason;\n this.socket = null;\n\n // flush all pending calls as error and clean up completed calls\n for (const id in this.calls) {\n const call = this.calls[id];\n\n if (!call.responseTime) {\n const errorMessage = this.connectionLostErrorMsg();\n this.triggerCallResponseCallback(\n id,\n new Error(errorMessage),\n null,\n );\n }\n }\n\n // Clean up completed calls to prevent memory leaks in long-running sessions\n for (const id in this.calls) {\n if (this.calls[id].responseTime) {\n delete this.calls[id];\n }\n }\n\n this.onDisconnect?.(reason);\n });\n\n setTimeout(() => {\n this.onConnect?.();\n\n const payload = {\n version: __VERSION__,\n } as BridgeConnectedEventPayload;\n socket.emit(BridgeEvent.Connected, payload);\n Promise.resolve().then(() => {\n for (const id in this.calls) {\n if (this.calls[id].callTime === 0) {\n this.emitCall(id);\n }\n }\n });\n }, 0);\n } catch (e) {\n logMsg(`failed to handle connection event: ${e}`);\n }\n });\n\n this.io.on('close', () => {\n this.close();\n });\n });\n }\n\n private connectionLostErrorMsg = () => {\n return `Connection lost, reason: ${this.connectionLostReason}`;\n };\n\n private async triggerCallResponseCallback(\n id: string | number,\n error: Error | string | null,\n response: any,\n ) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n // Ensure error is always an Error object (bridge client may send strings)\n if (error) {\n call.error =\n error instanceof Error\n ? error\n : new Error(typeof error === 'string' ? error : String(error));\n } else {\n call.error = undefined;\n }\n call.response = response;\n call.responseTime = Date.now();\n\n call.callback(call.error, response);\n }\n\n private async emitCall(id: string) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n\n if (this.connectionLost) {\n const message = `Connection lost, reason: ${this.connectionLostReason}`;\n call.callback(new Error(message), null);\n return;\n }\n\n if (this.socket) {\n this.socket.emit(BridgeEvent.Call, {\n id,\n method: call.method,\n args: call.args,\n });\n call.callTime = Date.now();\n }\n }\n\n async call<T = any>(\n method: string,\n args: any[],\n timeout = BridgeCallTimeout,\n ): Promise<T> {\n const id = `${this.callId++}`;\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n logMsg(`bridge call timeout, id=${id}, method=${method}, args=`, args);\n this.calls[id].error = new Error(\n `Bridge call timeout after ${timeout}ms: ${method}`,\n );\n reject(this.calls[id].error);\n }, timeout);\n\n this.calls[id] = {\n method,\n args,\n response: null,\n callTime: 0,\n responseTime: 0,\n callback: (error: Error | undefined, response: any) => {\n clearTimeout(timeoutId);\n if (error) {\n reject(error);\n } else {\n resolve(response);\n }\n },\n };\n\n this.emitCall(id);\n });\n }\n\n // do NOT restart after close\n async close() {\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n const closeProcess = this.io?.close();\n this.io = null;\n\n return closeProcess;\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debug","getDebug","isTrustedOrigin","origin","killRunningServer","port","host","client","ClientIO","DefaultBridgeServerPort","BridgeSignalKill","sleep","e","BridgeServer","opts","timeout","Promise","resolve","reject","Error","setTimeout","BridgeErrorCodeNoClientConnected","logMsg","httpServer","createServer","_req","res","err","Server","socket","next","url","console","clearTimeout","BridgeEvent","clientVersion","params","id","response","error","reason","call","errorMessage","payload","__VERSION__","String","undefined","Date","message","method","args","BridgeCallTimeout","timeoutId","closeProcess","onConnect","onDisconnect","closeConflictServer"],"mappings":";;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;;;;;ACgBA,MAAMI,QAAQC,AAAAA,IAAAA,uBAAAA,QAAAA,AAAAA,EAAS;AAavB,SAASC,gBAAgBC,MAA0B;IACjD,IAAI,CAACA,QAAQ,OAAO;IACpB,IAAIA,OAAO,UAAU,CAAC,wBAAwB,OAAO;IACrD,OAAO;AACT;AAEO,MAAMC,oBAAoB,OAAOC,MAAeC,OAAO,WAAW;IACvE,IAAI;QACF,MAAMC,SAASC,AAAAA,IAAAA,0CAAAA,EAAAA,AAAAA,EAAS,CAAC,KAAK,EAAEF,KAAK,CAAC,EAAED,QAAQI,mCAAAA,uBAAuBA,EAAE,EAAE;YACzE,OAAO;gBACL,CAACC,mCAAAA,gBAAgBA,CAAC,EAAE;YACtB;QACF;QACA,MAAMC,AAAAA,IAAAA,sBAAAA,KAAAA,AAAAA,EAAM;QACZ,MAAMJ,OAAO,KAAK;IACpB,EAAE,OAAOK,GAAG;QACVZ,MAAM,2BAA2BY;IACnC;AACF;AAGO,MAAMC;IAoBX,MAAM,OACJC,OAEI,CAAC,CAAC,EACS;QACf,MAAM,EAAEC,UAAU,KAAK,EAAE,GAAGD;QAE5B,IAAI,IAAI,CAAC,mBAAmB,EAC1B,MAAMV,kBAAkB,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI;QAG9C,OAAO,IAAIY,QAAQ,CAACC,SAASC;YAC3B,IAAI,IAAI,CAAC,kBAAkB,EACzB,OAAOA,OAAO,IAAIC,MAAM;YAE1B,IAAI,CAAC,kBAAkB,GAAG;YAE1B,IAAI,CAAC,kBAAkB,GAAGJ,UACtBK,WAAW;gBACTF,OACE,IAAIC,MACF,CAAC,6BAA6B,EAAEJ,QAAQ,IAAI,EAAEM,mCAAAA,gCAAgCA,CAAC,CAAC,CAAC;YAGvF,GAAGN,WACH;YAEJ,IAAI,CAAC,kBAAkB,GACrB,CAACA,WAAWA,UAAU,OAClBK,WAAW;gBACTE,IAAAA,6BAAAA,MAAAA,AAAAA,EAAO;YACT,GAAG,QACH;YAIN,MAAMC,aAAaC,AAAAA,IAAAA,mCAAAA,YAAAA,AAAAA,EACjB,CAACC,MAAuBC;gBACtBA,IAAI,SAAS,CAAC;gBACdA,IAAI,GAAG;YACT;YAIFH,WAAW,IAAI,CAAC,aAAa;gBAC3BN;YACF;YAEAM,WAAW,IAAI,CAAC,SAAS,CAACI;gBACxBT,OAAO,IAAIC,MAAM,CAAC,wBAAwB,EAAEQ,IAAI,OAAO,EAAE;YAC3D;YAKA,IAAI,AAAc,gBAAd,IAAI,CAAC,IAAI,EACXJ,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI;iBAE3BA,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI;YAIxC,IAAI,CAAC,EAAE,GAAG,IAAIK,mCAAAA,MAAMA,CAACL,YAAY;gBAC/B,mBAAmB;gBAInB,aAAa;YACf;YAEA,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAACM,QAAQC;gBAKnB,MAAM3B,SAAS0B,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM;gBAC9C,IAAI,CAAC3B,gBAAgBC,SAAS;oBAC5BH,MAAM,4CAA4CG;oBAClD,OAAO2B,KAAK,IAAIX,MAAM;gBACxB;gBAKA,IACEU,AAAoD,UAApDA,OAAO,SAAS,CAAC,GAAG,CAAC,QAAQ,CAACnB,mCAAAA,gBAAgBA,KAC9C,IAAI,CAAC,MAAM,EAAE,WAEb,OAAOoB,KAAK,IAAIX,MAAM;gBAExBW;YACF;YAEA,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,cAAc,CAACD;gBAExB,MAAME,MAAMF,OAAO,SAAS,CAAC,GAAG;gBAChC,IAAIE,IAAI,QAAQ,CAACrB,mCAAAA,gBAAgBA,GAAG;oBAClCsB,QAAQ,IAAI,CAAC;oBACb,OAAO,IAAI,CAAC,KAAK;gBACnB;gBAEA,IAAI,CAAC,cAAc,GAAG;gBACtB,IAAI,CAAC,oBAAoB,GAAG;gBAC5B,IAAI,CAAC,kBAAkB,IAAIC,aAAa,IAAI,CAAC,kBAAkB;gBAC/D,IAAI,CAAC,kBAAkB,GAAG;gBAC1B,IAAI,CAAC,kBAAkB,IAAIA,aAAa,IAAI,CAAC,kBAAkB;gBAC/D,IAAI,CAAC,kBAAkB,GAAG;gBAC1B,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW;oBAC1BJ,OAAO,IAAI,CAACK,mCAAAA,WAAAA,CAAAA,OAAmB;oBAC/BL,OAAO,UAAU;oBACjBP,IAAAA,6BAAAA,MAAAA,AAAAA,EACE;oBAEF;gBACF;gBAGA,IAAI,IAAI,CAAC,MAAM,EAAE;oBACf,IAAI;wBACF,IAAI,CAAC,MAAM,CAAC,UAAU;oBACxB,EAAE,OAAOV,GAAG;wBACVU,IAAAA,6BAAAA,MAAAA,AAAAA,EAAO,CAAC,mCAAmC,EAAEV,GAAG;oBAClD;oBACA,IAAI,CAAC,MAAM,GAAG;gBAChB;gBAEA,IAAI;oBACFU,IAAAA,6BAAAA,MAAAA,AAAAA,EAAO;oBACP,IAAI,CAAC,MAAM,GAAGO;oBAEd,MAAMM,gBAAgBN,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO;oBACpDP,IAAAA,6BAAAA,MAAAA,AAAAA,EACE,qEAA6Ea,eAAe;oBAG9FN,OAAO,EAAE,CAACK,mCAAAA,WAAAA,CAAAA,YAAwB,EAAE,CAACE;wBACnC,MAAMC,KAAKD,OAAO,EAAE;wBACpB,MAAME,WAAWF,OAAO,QAAQ;wBAChC,MAAMG,QAAQH,OAAO,KAAK;wBAE1B,IAAI,CAAC,2BAA2B,CAACC,IAAIE,OAAOD;oBAC9C;oBAEAT,OAAO,EAAE,CAAC,cAAc,CAACW;wBACvB,IAAI,CAAC,cAAc,GAAG;wBACtB,IAAI,CAAC,oBAAoB,GAAGA;wBAC5B,IAAI,CAAC,MAAM,GAAG;wBAGd,IAAK,MAAMH,MAAM,IAAI,CAAC,KAAK,CAAE;4BAC3B,MAAMI,OAAO,IAAI,CAAC,KAAK,CAACJ,GAAG;4BAE3B,IAAI,CAACI,KAAK,YAAY,EAAE;gCACtB,MAAMC,eAAe,IAAI,CAAC,sBAAsB;gCAChD,IAAI,CAAC,2BAA2B,CAC9BL,IACA,IAAIlB,MAAMuB,eACV;4BAEJ;wBACF;wBAGA,IAAK,MAAML,MAAM,IAAI,CAAC,KAAK,CACzB,IAAI,IAAI,CAAC,KAAK,CAACA,GAAG,CAAC,YAAY,EAC7B,OAAO,IAAI,CAAC,KAAK,CAACA,GAAG;wBAIzB,IAAI,CAAC,YAAY,GAAGG;oBACtB;oBAEApB,WAAW;wBACT,IAAI,CAAC,SAAS;wBAEd,MAAMuB,UAAU;4BACd,SAASC;wBACX;wBACAf,OAAO,IAAI,CAACK,mCAAAA,WAAAA,CAAAA,SAAqB,EAAES;wBACnC3B,QAAQ,OAAO,GAAG,IAAI,CAAC;4BACrB,IAAK,MAAMqB,MAAM,IAAI,CAAC,KAAK,CACzB,IAAI,AAA4B,MAA5B,IAAI,CAAC,KAAK,CAACA,GAAG,CAAC,QAAQ,EACzB,IAAI,CAAC,QAAQ,CAACA;wBAGpB;oBACF,GAAG;gBACL,EAAE,OAAOzB,GAAG;oBACVU,IAAAA,6BAAAA,MAAAA,AAAAA,EAAO,CAAC,mCAAmC,EAAEV,GAAG;gBAClD;YACF;YAEA,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS;gBAClB,IAAI,CAAC,KAAK;YACZ;QACF;IACF;IAMA,MAAc,4BACZyB,EAAmB,EACnBE,KAA4B,EAC5BD,QAAa,EACb;QACA,MAAMG,OAAO,IAAI,CAAC,KAAK,CAACJ,GAAG;QAC3B,IAAI,CAACI,MACH,MAAM,IAAItB,MAAM,CAAC,KAAK,EAAEkB,GAAG,UAAU,CAAC;QAGxC,IAAIE,OACFE,KAAK,KAAK,GACRF,iBAAiBpB,QACboB,QACA,IAAIpB,MAAM,AAAiB,YAAjB,OAAOoB,QAAqBA,QAAQM,OAAON;aAE3DE,KAAK,KAAK,GAAGK;QAEfL,KAAK,QAAQ,GAAGH;QAChBG,KAAK,YAAY,GAAGM,KAAK,GAAG;QAE5BN,KAAK,QAAQ,CAACA,KAAK,KAAK,EAAEH;IAC5B;IAEA,MAAc,SAASD,EAAU,EAAE;QACjC,MAAMI,OAAO,IAAI,CAAC,KAAK,CAACJ,GAAG;QAC3B,IAAI,CAACI,MACH,MAAM,IAAItB,MAAM,CAAC,KAAK,EAAEkB,GAAG,UAAU,CAAC;QAGxC,IAAI,IAAI,CAAC,cAAc,EAAE;YACvB,MAAMW,UAAU,CAAC,yBAAyB,EAAE,IAAI,CAAC,oBAAoB,EAAE;YACvEP,KAAK,QAAQ,CAAC,IAAItB,MAAM6B,UAAU;YAClC;QACF;QAEA,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAACd,mCAAAA,WAAAA,CAAAA,IAAgB,EAAE;gBACjCG;gBACA,QAAQI,KAAK,MAAM;gBACnB,MAAMA,KAAK,IAAI;YACjB;YACAA,KAAK,QAAQ,GAAGM,KAAK,GAAG;QAC1B;IACF;IAEA,MAAM,KACJE,MAAc,EACdC,IAAW,EACXnC,UAAUoC,mCAAAA,iBAAiB,EACf;QACZ,MAAMd,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI;QAE7B,OAAO,IAAIrB,QAAQ,CAACC,SAASC;YAC3B,MAAMkC,YAAYhC,WAAW;gBAC3BE,IAAAA,6BAAAA,MAAAA,AAAAA,EAAO,CAAC,wBAAwB,EAAEe,GAAG,SAAS,EAAEY,OAAO,OAAO,CAAC,EAAEC;gBACjE,IAAI,CAAC,KAAK,CAACb,GAAG,CAAC,KAAK,GAAG,IAAIlB,MACzB,CAAC,0BAA0B,EAAEJ,QAAQ,IAAI,EAAEkC,QAAQ;gBAErD/B,OAAO,IAAI,CAAC,KAAK,CAACmB,GAAG,CAAC,KAAK;YAC7B,GAAGtB;YAEH,IAAI,CAAC,KAAK,CAACsB,GAAG,GAAG;gBACfY;gBACAC;gBACA,UAAU;gBACV,UAAU;gBACV,cAAc;gBACd,UAAU,CAACX,OAA0BD;oBACnCL,aAAamB;oBACb,IAAIb,OACFrB,OAAOqB;yBAEPtB,QAAQqB;gBAEZ;YACF;YAEA,IAAI,CAAC,QAAQ,CAACD;QAChB;IACF;IAGA,MAAM,QAAQ;QACZ,IAAI,CAAC,kBAAkB,IAAIJ,aAAa,IAAI,CAAC,kBAAkB;QAC/D,IAAI,CAAC,kBAAkB,IAAIA,aAAa,IAAI,CAAC,kBAAkB;QAC/D,MAAMoB,eAAe,IAAI,CAAC,EAAE,EAAE;QAC9B,IAAI,CAAC,EAAE,GAAG;QAEV,OAAOA;IACT;IA5SA,YACS/C,IAAY,EACZD,IAAY,EACZiD,SAAsB,EACtBC,YAAuC,EACvCC,mBAA6B,CACpC;;;;;;QAjBF,uBAAQ,UAAR;QACA,uBAAQ,MAAR;QACA,uBAAQ,UAAR;QACA,uBAAQ,sBAAR;QACA,uBAAQ,sBAAR;QACA,uBAAQ,sBAAR;QACA,uBAAO,SAAP;QAEA,uBAAQ,kBAAR;QACA,uBAAQ,wBAAR;QAgNA,uBAAQ,0BAAR;aA7MSlD,IAAI,GAAJA;aACAD,IAAI,GAAJA;aACAiD,SAAS,GAATA;aACAC,YAAY,GAAZA;aACAC,mBAAmB,GAAnBA;aAhBD,MAAM,GAAG;aACT,EAAE,GAAkB;aACpB,MAAM,GAAwB;aAC9B,kBAAkB,GAA0B;aAC5C,kBAAkB,GAAG;aACrB,kBAAkB,GAA0B;aAC7C,KAAK,GAA+B,CAAC;aAEpC,cAAc,GAAG;aACjB,oBAAoB,GAAG;aAgNvB,sBAAsB,GAAG,IACxB,CAAC,yBAAyB,EAAE,IAAI,CAAC,oBAAoB,EAAE;IAzM7D;AAuSL"}
|
|
1
|
+
{"version":3,"file":"bridge-mode/io-server.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/bridge-mode/io-server.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n type IncomingMessage,\n type ServerResponse,\n createServer,\n} from 'node:http';\nimport { sleep } from '@midscene/core/utils';\nimport { getDebug } from '@midscene/shared/logger';\nimport { logMsg } from '@midscene/shared/utils';\nimport { Server, type Socket as ServerSocket } from 'socket.io';\nimport { io as ClientIO } from 'socket.io-client';\n\nimport {\n type BridgeCall,\n type BridgeCallResponse,\n BridgeCallTimeout,\n type BridgeConnectedEventPayload,\n BridgeErrorCodeNoClientConnected,\n BridgeEvent,\n BridgeSignalKill,\n DefaultBridgeServerHost,\n DefaultBridgeServerPort,\n} from './common';\n\nconst debug = getDebug('web:bridge:io-server');\n\ndeclare const __VERSION__: string;\n\n/**\n * Returns true if the given Origin header value is allowed to connect\n * to the bridge server. Trusted origins are:\n * - No Origin header (local Node.js processes like killRunningServer)\n * - chrome-extension:// origins (the Midscene Chrome extension)\n *\n * Browser pages (https://evil.com, etc.) are rejected to prevent\n * Cross-Site WebSocket Hijacking (CSWSH) attacks.\n */\nfunction isTrustedOrigin(origin: string | undefined): boolean {\n if (!origin) return true;\n if (origin.startsWith('chrome-extension://')) return true;\n return false;\n}\n\nexport const killRunningServer = async (\n port?: number,\n host = DefaultBridgeServerHost,\n) => {\n try {\n const client = ClientIO(`ws://${host}:${port || DefaultBridgeServerPort}`, {\n query: {\n [BridgeSignalKill]: 1,\n },\n });\n await sleep(300);\n await client.close();\n } catch (e) {\n debug('failed to kill port: %O', e);\n }\n};\n\n// ws server, this is where the request is sent\nexport class BridgeServer {\n private callId = 0;\n private io: Server | null = null;\n private socket: ServerSocket | null = null;\n private listeningTimeoutId: NodeJS.Timeout | null = null;\n private listeningTimerFlag = false;\n private connectionTipTimer: NodeJS.Timeout | null = null;\n public calls: Record<string, BridgeCall> = {};\n\n private connectionLost = false;\n private connectionLostReason = '';\n\n constructor(\n public host: string,\n public port: number,\n public onConnect?: () => void,\n public onDisconnect?: (reason: string) => void,\n public closeConflictServer?: boolean,\n ) {}\n\n async listen(\n opts: {\n timeout?: number | false;\n } = {},\n ): Promise<void> {\n const { timeout = 30000 } = opts;\n\n if (this.closeConflictServer) {\n await killRunningServer(this.port, this.host);\n }\n\n return new Promise((resolve, reject) => {\n if (this.listeningTimerFlag) {\n return reject(new Error('already listening'));\n }\n this.listeningTimerFlag = true;\n\n this.listeningTimeoutId = timeout\n ? setTimeout(() => {\n reject(\n new Error(\n `no extension connected after ${timeout}ms (${BridgeErrorCodeNoClientConnected})`,\n ),\n );\n }, timeout)\n : null;\n\n this.connectionTipTimer =\n !timeout || timeout > 3000\n ? setTimeout(() => {\n logMsg('waiting for bridge to connect...');\n }, 2000)\n : null;\n\n // Create HTTP server — no /bridge-auth endpoint needed since we use\n // pure Origin checking in the Socket.IO middleware below.\n const httpServer = createServer(\n (_req: IncomingMessage, res: ServerResponse) => {\n res.writeHead(404);\n res.end();\n },\n );\n\n // Set up HTTP server event listeners FIRST\n httpServer.once('listening', () => {\n resolve();\n });\n\n httpServer.once('error', (err: Error) => {\n reject(new Error(`Bridge Listening Error: ${err.message}`));\n });\n\n // Always honor the configured interface, including the loopback default.\n httpServer.listen(this.port, this.host);\n\n // Now create Socket.IO Server attached to the already-listening HTTP server\n this.io = new Server(httpServer, {\n maxHttpBufferSize: 100 * 1024 * 1024, // 100MB\n // Increase pingTimeout to tolerate Chrome MV3 Service Worker suspension.\n // The SW keepalive alarm fires every ~24s; default pingTimeout (20s) may\n // be too short if the SW is suspended between alarm pings.\n pingTimeout: 60000,\n });\n\n this.io.use((socket, next) => {\n // Reject connections from untrusted browser origins to prevent\n // Cross-Site WebSocket Hijacking (CSWSH) and unauthorized kill signals.\n // The browser automatically sets the Origin header on WebSocket\n // handshakes, and JavaScript cannot override it.\n const origin = socket.handshake.headers.origin;\n if (!isTrustedOrigin(origin)) {\n debug('connection rejected: untrusted origin=%s', origin);\n return next(new Error('origin not allowed'));\n }\n\n // Allow new connections to replace old ones (reconnection after\n // extension Stop→Start). If the old socket is already disconnected\n // or unresponsive, accept the new connection immediately.\n if (\n socket.handshake.url.includes(BridgeSignalKill) === false &&\n this.socket?.connected\n ) {\n return next(new Error('server already connected by another client'));\n }\n next();\n });\n\n this.io.on('connection', (socket) => {\n // check the connection url\n const url = socket.handshake.url;\n if (url.includes(BridgeSignalKill)) {\n console.warn('kill signal received, closing bridge server');\n return this.close();\n }\n\n this.connectionLost = false;\n this.connectionLostReason = '';\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.listeningTimeoutId = null;\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n this.connectionTipTimer = null;\n if (this.socket?.connected) {\n socket.emit(BridgeEvent.Refused);\n socket.disconnect();\n logMsg(\n 'refused new connection: server already connected by another client',\n );\n return;\n }\n\n // Clean up stale old socket if it exists but is no longer connected\n if (this.socket) {\n try {\n this.socket.disconnect();\n } catch (e) {\n logMsg(`failed to disconnect stale socket: ${e}`);\n }\n this.socket = null;\n }\n\n try {\n logMsg('one client connected');\n this.socket = socket;\n\n const clientVersion = socket.handshake.query.version;\n logMsg(\n `Bridge connected, cli-side version v${__VERSION__}, browser-side version v${clientVersion}`,\n );\n\n socket.on(BridgeEvent.CallResponse, (params: BridgeCallResponse) => {\n const id = params.id;\n const response = params.response;\n const error = params.error;\n\n this.triggerCallResponseCallback(id, error, response);\n });\n\n socket.on('disconnect', (reason: string) => {\n this.connectionLost = true;\n this.connectionLostReason = reason;\n this.socket = null;\n\n // flush all pending calls as error and clean up completed calls\n for (const id in this.calls) {\n const call = this.calls[id];\n\n if (!call.responseTime) {\n const errorMessage = this.connectionLostErrorMsg();\n this.triggerCallResponseCallback(\n id,\n new Error(errorMessage),\n null,\n );\n }\n }\n\n // Clean up completed calls to prevent memory leaks in long-running sessions\n for (const id in this.calls) {\n if (this.calls[id].responseTime) {\n delete this.calls[id];\n }\n }\n\n this.onDisconnect?.(reason);\n });\n\n setTimeout(() => {\n this.onConnect?.();\n\n const payload = {\n version: __VERSION__,\n } as BridgeConnectedEventPayload;\n socket.emit(BridgeEvent.Connected, payload);\n Promise.resolve().then(() => {\n for (const id in this.calls) {\n if (this.calls[id].callTime === 0) {\n this.emitCall(id);\n }\n }\n });\n }, 0);\n } catch (e) {\n logMsg(`failed to handle connection event: ${e}`);\n }\n });\n\n this.io.on('close', () => {\n this.close();\n });\n });\n }\n\n private connectionLostErrorMsg = () => {\n return `Connection lost, reason: ${this.connectionLostReason}`;\n };\n\n private async triggerCallResponseCallback(\n id: string | number,\n error: Error | string | null,\n response: any,\n ) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n // Ensure error is always an Error object (bridge client may send strings)\n if (error) {\n call.error =\n error instanceof Error\n ? error\n : new Error(typeof error === 'string' ? error : String(error));\n } else {\n call.error = undefined;\n }\n call.response = response;\n call.responseTime = Date.now();\n\n call.callback(call.error, response);\n }\n\n private async emitCall(id: string) {\n const call = this.calls[id];\n if (!call) {\n throw new Error(`call ${id} not found`);\n }\n\n if (this.connectionLost) {\n const message = `Connection lost, reason: ${this.connectionLostReason}`;\n call.callback(new Error(message), null);\n return;\n }\n\n if (this.socket) {\n this.socket.emit(BridgeEvent.Call, {\n id,\n method: call.method,\n args: call.args,\n });\n call.callTime = Date.now();\n }\n }\n\n async call<T = any>(\n method: string,\n args: any[],\n timeout = BridgeCallTimeout,\n ): Promise<T> {\n const id = `${this.callId++}`;\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n logMsg(`bridge call timeout, id=${id}, method=${method}, args=`, args);\n this.calls[id].error = new Error(\n `Bridge call timeout after ${timeout}ms: ${method}`,\n );\n reject(this.calls[id].error);\n }, timeout);\n\n this.calls[id] = {\n method,\n args,\n response: null,\n callTime: 0,\n responseTime: 0,\n callback: (error: Error | undefined, response: any) => {\n clearTimeout(timeoutId);\n if (error) {\n reject(error);\n } else {\n resolve(response);\n }\n },\n };\n\n this.emitCall(id);\n });\n }\n\n // do NOT restart after close\n async close() {\n this.listeningTimeoutId && clearTimeout(this.listeningTimeoutId);\n this.connectionTipTimer && clearTimeout(this.connectionTipTimer);\n const closeProcess = this.io?.close();\n this.io = null;\n\n return closeProcess;\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debug","getDebug","isTrustedOrigin","origin","killRunningServer","port","host","DefaultBridgeServerHost","client","ClientIO","DefaultBridgeServerPort","BridgeSignalKill","sleep","e","BridgeServer","opts","timeout","Promise","resolve","reject","Error","setTimeout","BridgeErrorCodeNoClientConnected","logMsg","httpServer","createServer","_req","res","err","Server","socket","next","url","console","clearTimeout","BridgeEvent","clientVersion","params","id","response","error","reason","call","errorMessage","payload","__VERSION__","String","undefined","Date","message","method","args","BridgeCallTimeout","timeoutId","closeProcess","onConnect","onDisconnect","closeConflictServer"],"mappings":"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"}
|
|
@@ -87,7 +87,7 @@ function waitForTabNavigationComplete(tabId, targetUrl, timeoutMs = NEW_TAB_LOAD
|
|
|
87
87
|
}
|
|
88
88
|
class ExtensionBridgePageBrowserSide extends page_js_default() {
|
|
89
89
|
async setupBridgeClient() {
|
|
90
|
-
const endpoint = this.serverEndpoint || `ws
|
|
90
|
+
const endpoint = this.serverEndpoint || `ws://${external_common_js_namespaceObject.DefaultBridgeServerHost}:${external_common_js_namespaceObject.DefaultBridgeServerPort}`;
|
|
91
91
|
let resolveConfirmationGate = ()=>{};
|
|
92
92
|
if (this.onConnectionRequest) this.confirmationPromise = new Promise((resolve)=>{
|
|
93
93
|
resolveConfirmationGate = resolve;
|
|
@@ -138,7 +138,7 @@ class ExtensionBridgePageBrowserSide extends page_js_default() {
|
|
|
138
138
|
throw new Error('Connection denied by user');
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.12.
|
|
141
|
+
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.12.5-beta-20260907092148.0`, 'log');
|
|
142
142
|
}
|
|
143
143
|
async connect() {
|
|
144
144
|
return await this.setupBridgeClient();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-mode/page-browser-side.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/bridge-mode/page-browser-side.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { assert } from '@midscene/shared/utils';\nimport ChromeExtensionProxyPage from '../chrome-extension/page';\nimport type {\n ChromePageDestroyOptions,\n KeyboardAction,\n MouseAction,\n} from '../web-page';\nimport {\n type BridgeConnectTabOptions,\n BridgeEvent,\n DefaultBridgeServerPort,\n KeyboardEvent,\n MouseEvent,\n} from './common';\nimport { BridgeClient } from './io-client';\n\ndeclare const __VERSION__: string;\n\nconst NEW_TAB_LOAD_TIMEOUT_MS = 30_000;\n\nfunction isBlankUrl(url: string | undefined): boolean {\n if (!url) return true;\n return url === 'about:blank' || url.startsWith('chrome://newtab');\n}\n\n// Wait until the freshly created tab has navigated away from about:blank\n// and reached `status === 'complete'`. Resolves on timeout instead of\n// throwing so callers degrade to the existing lazy-attach behavior.\nfunction waitForTabNavigationComplete(\n tabId: number,\n targetUrl: string,\n timeoutMs = NEW_TAB_LOAD_TIMEOUT_MS,\n): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n try {\n chrome.tabs.onUpdated.removeListener(onUpdated);\n } catch {}\n clearTimeout(timer);\n resolve();\n };\n\n const isReady = (tab: chrome.tabs.Tab | undefined): boolean => {\n if (!tab) return false;\n if (tab.status !== 'complete') return false;\n const currentUrl = tab.url || tab.pendingUrl || '';\n // Skip the initial about:blank \"complete\" that fires before\n // the target URL navigation kicks in.\n if (isBlankUrl(currentUrl) && !isBlankUrl(targetUrl)) return false;\n return true;\n };\n\n const onUpdated = (\n id: number,\n _info: chrome.tabs.TabChangeInfo,\n tab: chrome.tabs.Tab,\n ) => {\n if (id !== tabId) return;\n if (isReady(tab)) finish();\n };\n\n chrome.tabs.onUpdated.addListener(onUpdated);\n const timer = setTimeout(finish, timeoutMs);\n\n // Handle the race where the tab already finished loading before\n // we registered the listener.\n chrome.tabs\n .get(tabId)\n .then((tab) => {\n if (isReady(tab)) finish();\n })\n .catch(() => {});\n });\n}\n\nexport class ExtensionBridgePageBrowserSide extends ChromeExtensionProxyPage {\n public bridgeClient: BridgeClient | null = null;\n\n private destroyOptions?: ChromePageDestroyOptions;\n\n private newlyCreatedTabIds: number[] = [];\n\n // Connection confirmation state\n private confirmationPromise: Promise<boolean> | null = null;\n\n constructor(\n public serverEndpoint?: string,\n public onDisconnect: () => void = () => {},\n public onLogMessage: (\n message: string,\n type: 'log' | 'status',\n ) => void = () => {},\n forceSameTabNavigation = true,\n public onConnectionRequest?: () => Promise<boolean>,\n ) {\n super(forceSameTabNavigation);\n }\n\n private async setupBridgeClient() {\n const endpoint =\n this.serverEndpoint || `ws://localhost:${DefaultBridgeServerPort}`;\n\n // Create confirmation gate BEFORE establishing connection,\n // so that any calls received immediately after connection are blocked\n // until user confirms. This prevents a race condition where server-side\n // queued calls bypass the confirmation dialog.\n let resolveConfirmationGate: (allowed: boolean) => void = () => {};\n if (this.onConnectionRequest) {\n this.confirmationPromise = new Promise<boolean>((resolve) => {\n resolveConfirmationGate = resolve;\n });\n }\n\n this.bridgeClient = new BridgeClient(\n endpoint,\n async (method, args: any[]) => {\n // Wait for user confirmation before processing any commands\n if (this.confirmationPromise) {\n const allowed = await this.confirmationPromise;\n if (!allowed) {\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(`bridge call from cli side: ${method}`, 'log');\n if (method === BridgeEvent.ConnectNewTabWithUrl) {\n return this.connectNewTabWithUrl.apply(\n this,\n args as unknown as [string],\n );\n }\n\n if (method === BridgeEvent.GetBrowserTabList) {\n return this.getBrowserTabList.apply(this, args as any);\n }\n\n if (method === BridgeEvent.SetActiveTabId) {\n return this.setActiveTabId.apply(this, args as any);\n }\n\n if (method === BridgeEvent.ConnectCurrentTab) {\n return this.connectCurrentTab.apply(this, args as any);\n }\n\n if (method === BridgeEvent.UpdateAgentStatus) {\n return this.onLogMessage(args[0] as string, 'status');\n }\n\n const tabId = await this.getActiveTabId();\n if (!tabId || tabId === 0) {\n throw new Error('no tab is connected');\n }\n\n // this.onLogMessage(`calling method: ${method}`);\n\n if (method.startsWith(MouseEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof MouseAction;\n if (actionName === 'drag') {\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n\n if (method.startsWith(KeyboardEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof KeyboardAction;\n if (actionName === 'press') {\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n\n // Generic method lookup. Methods like `setWaterFlowAnimationEnabled`\n // and `getWaterFlowAnimationEnabled` are inherited from\n // ChromeExtensionProxyPage and found via `this[method]`.\n if (!this[method as keyof ChromeExtensionProxyPage]) {\n this.onLogMessage(`method not found: ${method}`, 'log');\n return undefined;\n }\n\n try {\n // @ts-expect-error\n const result = await this[method as keyof ChromeExtensionProxyPage](\n ...args,\n );\n return result;\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Unknown error';\n this.onLogMessage(\n `Error calling method: ${method}, ${errorMessage}`,\n 'log',\n );\n throw new Error(errorMessage, { cause: e });\n }\n },\n // on disconnect\n () => {\n return this.destroy();\n },\n );\n await this.bridgeClient.connect();\n\n // Show confirmation dialog after connection is established\n if (this.onConnectionRequest) {\n this.onLogMessage('Waiting for user confirmation...', 'log');\n const allowed = await this.onConnectionRequest();\n resolveConfirmationGate(allowed);\n this.confirmationPromise = null;\n\n if (!allowed) {\n this.onLogMessage('Connection denied by user', 'log');\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(\n `Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v${__VERSION__}`,\n 'log',\n );\n }\n\n public async connect() {\n return await this.setupBridgeClient();\n }\n\n public async connectNewTabWithUrl(\n url: string,\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n // Otherwise the animation gets injected during the connection flow\n // before the CLI side's setWaterFlowAnimationEnabled(false) arrives.\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n // Preserve the historical default: activate the new tab unless the caller opts into background mode.\n const activate = options.activateTab ?? true;\n const tab = await chrome.tabs.create({ url, active: activate });\n const tabId = tab.id;\n assert(tabId, 'failed to get tabId after creating a new tab');\n\n // new tab\n this.onLogMessage(`Creating new tab: ${url}`, 'log');\n this.newlyCreatedTabIds.push(tabId);\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n // chrome.tabs.create returns immediately with an about:blank target,\n // then navigates to `url`. If we attach the debugger during that\n // cross-origin transition Site Isolation will detach it again, leaving\n // the first CDP command to fail with \"Debugger is not attached to the\n // tab\". Wait for navigation to settle so the lazy attach lands on a\n // stable target.\n await waitForTabNavigationComplete(tabId, url);\n\n await this.setActiveTabId(tabId, { activate });\n }\n\n public async connectCurrentTab(\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n const tabs = await chrome.tabs.query({ active: true, currentWindow: true });\n const tabId = tabs[0]?.id;\n assert(tabId, 'failed to get tabId');\n\n this.onLogMessage(`Connected to current tab: ${tabs[0]?.url}`, 'log');\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n await this.setActiveTabId(tabId);\n }\n\n public async setDestroyOptions(options: ChromePageDestroyOptions) {\n this.destroyOptions = options;\n }\n\n async destroy() {\n if (this.destroyOptions?.closeTab && this.newlyCreatedTabIds.length > 0) {\n this.onLogMessage('Closing all newly created tabs by bridge...', 'log');\n for (const tabId of this.newlyCreatedTabIds) {\n await chrome.tabs.remove(tabId);\n }\n this.newlyCreatedTabIds = [];\n }\n\n await super.destroy();\n\n if (this.bridgeClient) {\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n this.onDisconnect();\n }\n }\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","NEW_TAB_LOAD_TIMEOUT_MS","isBlankUrl","url","waitForTabNavigationComplete","tabId","targetUrl","timeoutMs","Promise","resolve","settled","finish","chrome","onUpdated","clearTimeout","timer","isReady","tab","currentUrl","id","_info","setTimeout","ExtensionBridgePageBrowserSide","ChromeExtensionProxyPage","endpoint","DefaultBridgeServerPort","resolveConfirmationGate","BridgeClient","method","args","allowed","Error","BridgeEvent","MouseEvent","actionName","KeyboardEvent","result","e","errorMessage","options","undefined","activate","assert","tabs","serverEndpoint","onDisconnect","onLogMessage","forceSameTabNavigation","onConnectionRequest"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bridge-mode/page-browser-side.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/bridge-mode/page-browser-side.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { assert } from '@midscene/shared/utils';\nimport ChromeExtensionProxyPage from '../chrome-extension/page';\nimport type {\n ChromePageDestroyOptions,\n KeyboardAction,\n MouseAction,\n} from '../web-page';\nimport {\n type BridgeConnectTabOptions,\n BridgeEvent,\n DefaultBridgeServerHost,\n DefaultBridgeServerPort,\n KeyboardEvent,\n MouseEvent,\n} from './common';\nimport { BridgeClient } from './io-client';\n\ndeclare const __VERSION__: string;\n\nconst NEW_TAB_LOAD_TIMEOUT_MS = 30_000;\n\nfunction isBlankUrl(url: string | undefined): boolean {\n if (!url) return true;\n return url === 'about:blank' || url.startsWith('chrome://newtab');\n}\n\n// Wait until the freshly created tab has navigated away from about:blank\n// and reached `status === 'complete'`. Resolves on timeout instead of\n// throwing so callers degrade to the existing lazy-attach behavior.\nfunction waitForTabNavigationComplete(\n tabId: number,\n targetUrl: string,\n timeoutMs = NEW_TAB_LOAD_TIMEOUT_MS,\n): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n try {\n chrome.tabs.onUpdated.removeListener(onUpdated);\n } catch {}\n clearTimeout(timer);\n resolve();\n };\n\n const isReady = (tab: chrome.tabs.Tab | undefined): boolean => {\n if (!tab) return false;\n if (tab.status !== 'complete') return false;\n const currentUrl = tab.url || tab.pendingUrl || '';\n // Skip the initial about:blank \"complete\" that fires before\n // the target URL navigation kicks in.\n if (isBlankUrl(currentUrl) && !isBlankUrl(targetUrl)) return false;\n return true;\n };\n\n const onUpdated = (\n id: number,\n _info: chrome.tabs.TabChangeInfo,\n tab: chrome.tabs.Tab,\n ) => {\n if (id !== tabId) return;\n if (isReady(tab)) finish();\n };\n\n chrome.tabs.onUpdated.addListener(onUpdated);\n const timer = setTimeout(finish, timeoutMs);\n\n // Handle the race where the tab already finished loading before\n // we registered the listener.\n chrome.tabs\n .get(tabId)\n .then((tab) => {\n if (isReady(tab)) finish();\n })\n .catch(() => {});\n });\n}\n\nexport class ExtensionBridgePageBrowserSide extends ChromeExtensionProxyPage {\n public bridgeClient: BridgeClient | null = null;\n\n private destroyOptions?: ChromePageDestroyOptions;\n\n private newlyCreatedTabIds: number[] = [];\n\n // Connection confirmation state\n private confirmationPromise: Promise<boolean> | null = null;\n\n constructor(\n public serverEndpoint?: string,\n public onDisconnect: () => void = () => {},\n public onLogMessage: (\n message: string,\n type: 'log' | 'status',\n ) => void = () => {},\n forceSameTabNavigation = true,\n public onConnectionRequest?: () => Promise<boolean>,\n ) {\n super(forceSameTabNavigation);\n }\n\n private async setupBridgeClient() {\n const endpoint =\n this.serverEndpoint ||\n `ws://${DefaultBridgeServerHost}:${DefaultBridgeServerPort}`;\n\n // Create confirmation gate BEFORE establishing connection,\n // so that any calls received immediately after connection are blocked\n // until user confirms. This prevents a race condition where server-side\n // queued calls bypass the confirmation dialog.\n let resolveConfirmationGate: (allowed: boolean) => void = () => {};\n if (this.onConnectionRequest) {\n this.confirmationPromise = new Promise<boolean>((resolve) => {\n resolveConfirmationGate = resolve;\n });\n }\n\n this.bridgeClient = new BridgeClient(\n endpoint,\n async (method, args: any[]) => {\n // Wait for user confirmation before processing any commands\n if (this.confirmationPromise) {\n const allowed = await this.confirmationPromise;\n if (!allowed) {\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(`bridge call from cli side: ${method}`, 'log');\n if (method === BridgeEvent.ConnectNewTabWithUrl) {\n return this.connectNewTabWithUrl.apply(\n this,\n args as unknown as [string],\n );\n }\n\n if (method === BridgeEvent.GetBrowserTabList) {\n return this.getBrowserTabList.apply(this, args as any);\n }\n\n if (method === BridgeEvent.SetActiveTabId) {\n return this.setActiveTabId.apply(this, args as any);\n }\n\n if (method === BridgeEvent.ConnectCurrentTab) {\n return this.connectCurrentTab.apply(this, args as any);\n }\n\n if (method === BridgeEvent.UpdateAgentStatus) {\n return this.onLogMessage(args[0] as string, 'status');\n }\n\n const tabId = await this.getActiveTabId();\n if (!tabId || tabId === 0) {\n throw new Error('no tab is connected');\n }\n\n // this.onLogMessage(`calling method: ${method}`);\n\n if (method.startsWith(MouseEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof MouseAction;\n if (actionName === 'drag') {\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n return this.mouse[actionName].apply(this.mouse, args as any);\n }\n\n if (method.startsWith(KeyboardEvent.PREFIX)) {\n const actionName = method.split('.')[1] as keyof KeyboardAction;\n if (actionName === 'press') {\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n return this.keyboard[actionName].apply(this.keyboard, args as any);\n }\n\n // Generic method lookup. Methods like `setWaterFlowAnimationEnabled`\n // and `getWaterFlowAnimationEnabled` are inherited from\n // ChromeExtensionProxyPage and found via `this[method]`.\n if (!this[method as keyof ChromeExtensionProxyPage]) {\n this.onLogMessage(`method not found: ${method}`, 'log');\n return undefined;\n }\n\n try {\n // @ts-expect-error\n const result = await this[method as keyof ChromeExtensionProxyPage](\n ...args,\n );\n return result;\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Unknown error';\n this.onLogMessage(\n `Error calling method: ${method}, ${errorMessage}`,\n 'log',\n );\n throw new Error(errorMessage, { cause: e });\n }\n },\n // on disconnect\n () => {\n return this.destroy();\n },\n );\n await this.bridgeClient.connect();\n\n // Show confirmation dialog after connection is established\n if (this.onConnectionRequest) {\n this.onLogMessage('Waiting for user confirmation...', 'log');\n const allowed = await this.onConnectionRequest();\n resolveConfirmationGate(allowed);\n this.confirmationPromise = null;\n\n if (!allowed) {\n this.onLogMessage('Connection denied by user', 'log');\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n throw new Error('Connection denied by user');\n }\n }\n\n this.onLogMessage(\n `Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v${__VERSION__}`,\n 'log',\n );\n }\n\n public async connect() {\n return await this.setupBridgeClient();\n }\n\n public async connectNewTabWithUrl(\n url: string,\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n // Otherwise the animation gets injected during the connection flow\n // before the CLI side's setWaterFlowAnimationEnabled(false) arrives.\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n // Preserve the historical default: activate the new tab unless the caller opts into background mode.\n const activate = options.activateTab ?? true;\n const tab = await chrome.tabs.create({ url, active: activate });\n const tabId = tab.id;\n assert(tabId, 'failed to get tabId after creating a new tab');\n\n // new tab\n this.onLogMessage(`Creating new tab: ${url}`, 'log');\n this.newlyCreatedTabIds.push(tabId);\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n // chrome.tabs.create returns immediately with an about:blank target,\n // then navigates to `url`. If we attach the debugger during that\n // cross-origin transition Site Isolation will detach it again, leaving\n // the first CDP command to fail with \"Debugger is not attached to the\n // tab\". Wait for navigation to settle so the lazy attach lands on a\n // stable target.\n await waitForTabNavigationComplete(tabId, url);\n\n await this.setActiveTabId(tabId, { activate });\n }\n\n public async connectCurrentTab(\n options: BridgeConnectTabOptions = {\n forceSameTabNavigation: true,\n },\n ) {\n // Set the water-flow animation flag BEFORE any debugger commands are\n // sent (which trigger enableWaterFlowAnimation via sendCommandToDebugger).\n if (options.enableWaterFlowAnimation !== undefined) {\n this.waterFlowAnimationEnabled = options.enableWaterFlowAnimation;\n }\n\n const tabs = await chrome.tabs.query({ active: true, currentWindow: true });\n const tabId = tabs[0]?.id;\n assert(tabId, 'failed to get tabId');\n\n this.onLogMessage(`Connected to current tab: ${tabs[0]?.url}`, 'log');\n\n if (options?.forceSameTabNavigation) {\n this.forceSameTabNavigation = true;\n }\n\n await this.setActiveTabId(tabId);\n }\n\n public async setDestroyOptions(options: ChromePageDestroyOptions) {\n this.destroyOptions = options;\n }\n\n async destroy() {\n if (this.destroyOptions?.closeTab && this.newlyCreatedTabIds.length > 0) {\n this.onLogMessage('Closing all newly created tabs by bridge...', 'log');\n for (const tabId of this.newlyCreatedTabIds) {\n await chrome.tabs.remove(tabId);\n }\n this.newlyCreatedTabIds = [];\n }\n\n await super.destroy();\n\n if (this.bridgeClient) {\n this.bridgeClient.disconnect();\n this.bridgeClient = null;\n this.onDisconnect();\n }\n }\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","NEW_TAB_LOAD_TIMEOUT_MS","isBlankUrl","url","waitForTabNavigationComplete","tabId","targetUrl","timeoutMs","Promise","resolve","settled","finish","chrome","onUpdated","clearTimeout","timer","isReady","tab","currentUrl","id","_info","setTimeout","ExtensionBridgePageBrowserSide","ChromeExtensionProxyPage","endpoint","DefaultBridgeServerHost","DefaultBridgeServerPort","resolveConfirmationGate","BridgeClient","method","args","allowed","Error","BridgeEvent","MouseEvent","actionName","KeyboardEvent","result","e","errorMessage","options","undefined","activate","assert","tabs","serverEndpoint","onDisconnect","onLogMessage","forceSameTabNavigation","onConnectionRequest"],"mappings":"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"}
|
package/dist/lib/cli.js
CHANGED
|
@@ -42,7 +42,7 @@ Promise.resolve().then(()=>{
|
|
|
42
42
|
return (0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-web', {
|
|
43
43
|
stripPrefix: 'web_',
|
|
44
44
|
argv: parsedOptions.argv,
|
|
45
|
-
version: "1.12.
|
|
45
|
+
version: "1.12.5-beta-20260907092148.0",
|
|
46
46
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
47
47
|
});
|
|
48
48
|
}).catch((e)=>{
|
|
@@ -24,15 +24,27 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
|
|
28
|
-
PlaywrightPageAgent: ()=>external_page_agent_js_namespaceObject.PlaywrightPageAgent
|
|
27
|
+
PlaywrightWebPage: ()=>external_page_js_namespaceObject.WebPage,
|
|
28
|
+
PlaywrightPageAgent: ()=>external_page_agent_js_namespaceObject.PlaywrightPageAgent,
|
|
29
|
+
overrideAIConfig: ()=>env_namespaceObject.overrideAIConfig,
|
|
30
|
+
PlaywrightAgent: ()=>external_page_agent_js_namespaceObject.PlaywrightAgent,
|
|
31
|
+
PlaywrightBrowserAgent: ()=>external_browser_agent_js_namespaceObject.PlaywrightBrowserAgent
|
|
29
32
|
});
|
|
33
|
+
const env_namespaceObject = require("@midscene/shared/env");
|
|
34
|
+
const external_page_js_namespaceObject = require("./page.js");
|
|
30
35
|
const external_page_agent_js_namespaceObject = require("./page-agent.js");
|
|
36
|
+
const external_browser_agent_js_namespaceObject = require("./browser-agent.js");
|
|
31
37
|
exports.PlaywrightAgent = __webpack_exports__.PlaywrightAgent;
|
|
38
|
+
exports.PlaywrightBrowserAgent = __webpack_exports__.PlaywrightBrowserAgent;
|
|
32
39
|
exports.PlaywrightPageAgent = __webpack_exports__.PlaywrightPageAgent;
|
|
40
|
+
exports.PlaywrightWebPage = __webpack_exports__.PlaywrightWebPage;
|
|
41
|
+
exports.overrideAIConfig = __webpack_exports__.overrideAIConfig;
|
|
33
42
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
34
43
|
"PlaywrightAgent",
|
|
35
|
-
"
|
|
44
|
+
"PlaywrightBrowserAgent",
|
|
45
|
+
"PlaywrightPageAgent",
|
|
46
|
+
"PlaywrightWebPage",
|
|
47
|
+
"overrideAIConfig"
|
|
36
48
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
37
49
|
Object.defineProperty(exports, '__esModule', {
|
|
38
50
|
value: true
|
|
@@ -28,12 +28,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
28
28
|
midsceneDumpAnnotationId: ()=>midsceneDumpAnnotationId
|
|
29
29
|
});
|
|
30
30
|
const browser_agent_js_namespaceObject = require("../common/browser-agent.js");
|
|
31
|
-
const external_index_js_namespaceObject = require("./index.js");
|
|
32
31
|
const utils_namespaceObject = require("@midscene/core/utils");
|
|
33
32
|
const constants_namespaceObject = require("@midscene/shared/constants");
|
|
34
33
|
const logger_namespaceObject = require("@midscene/shared/logger");
|
|
35
34
|
const shared_utils_namespaceObject = require("@midscene/shared/utils");
|
|
36
35
|
const test_namespaceObject = require("@playwright/test");
|
|
36
|
+
const external_agent_js_namespaceObject = require("./agent.js");
|
|
37
37
|
const external_report_filename_js_namespaceObject = require("./report-filename.js");
|
|
38
38
|
const debugPage = (0, logger_namespaceObject.getDebug)('web:playwright:ai-fixture');
|
|
39
39
|
const groupAndCaseForTest = (testInfo)=>{
|
|
@@ -118,10 +118,10 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
118
118
|
...sharedAgentOptions,
|
|
119
119
|
...opts
|
|
120
120
|
};
|
|
121
|
-
const agent = autoFollowNewPage ? new
|
|
121
|
+
const agent = autoFollowNewPage ? new external_agent_js_namespaceObject.PlaywrightBrowserAgent(page.context(), page, {
|
|
122
122
|
...commonAgentOpts,
|
|
123
123
|
autoFollowNewPage: runtimeOptions.autoFollowNewPage
|
|
124
|
-
}) : new
|
|
124
|
+
}) : new external_agent_js_namespaceObject.PlaywrightAgent(page, {
|
|
125
125
|
...commonAgentOpts,
|
|
126
126
|
forceSameTabNavigation: runtimeOptions.forceSameTabNavigation
|
|
127
127
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/ai-fixture.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/ai-fixture.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { resolveBrowserAgentRuntimeOptions } from '@/common/browser-agent';\nimport {\n PlaywrightAgent,\n PlaywrightBrowserAgent,\n type PlaywrightWebPage,\n} from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { replaceIllegalPathCharsAndSpace, uuid } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nimport { buildPlaywrightReportTag } from './report-filename';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\ntype AgentRecord = {\n agent: PageAgent<PlaywrightWebPage>;\n finalizePromise?: Promise<string | undefined>;\n finalReportPath?: string;\n};\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport type PlaywrightAiFixtureOptions = Omit<\n WebPageAgentOpt,\n | 'testId'\n | 'cacheId'\n | 'groupName'\n | 'groupDescription'\n | 'reportFileName'\n | 'cache'\n> & {\n autoFollowNewPage?: boolean;\n cache?: PlaywrightCache;\n};\n\nexport const PlaywrightAiFixture = (options?: PlaywrightAiFixtureOptions) => {\n const {\n forceSameTabNavigation = true,\n autoFollowNewPage = false,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n ...sharedAgentOptions\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const testAgentRecords = new Map<string, Map<string, AgentRecord>>();\n\n const getAgentRecordsForTest = (testInfo: TestInfo) => {\n let records = testAgentRecords.get(testInfo.testId);\n if (!records) {\n records = new Map<string, AgentRecord>();\n testAgentRecords.set(testInfo.testId, records);\n }\n return records;\n };\n\n const setReportAnnotation = (testInfo: TestInfo, reportPaths: string[]) => {\n testInfo.annotations = testInfo.annotations.filter((item) => {\n return item.type !== midsceneDumpAnnotationId;\n });\n\n for (const reportPath of reportPaths) {\n testInfo.annotations.push({\n type: midsceneDumpAnnotationId,\n description: reportPath,\n });\n }\n };\n\n const finalizeAgentRecord = async (\n record: AgentRecord,\n ): Promise<string | undefined> => {\n if (!record.finalizePromise) {\n record.finalizePromise = (async () => {\n await record.agent.destroy();\n const reportPath = record.agent.reportFile || undefined;\n record.finalReportPath = reportPath;\n return reportPath;\n })();\n }\n\n return await record.finalizePromise;\n };\n\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n // `replaceIllegalPathCharsAndSpace` intentionally preserves `/` and `\\`\n // so groupName/groupDescription can still carry hierarchy. But\n // ReportGenerator rejects path separators in the file name, so the\n // report tag builder strips them without changing the group metadata.\n const reportTag = buildPlaywrightReportTag(title, idForPage);\n\n if (autoFollowNewPage && forceSameTabNavigation === true) {\n throw new Error(\n '[midscene] autoFollowNewPage cannot be used with forceSameTabNavigation: true.',\n );\n }\n\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightAiFixture',\n pageScope: autoFollowNewPage ? 'browser' : 'page',\n forceSameTabNavigation: autoFollowNewPage\n ? undefined\n : forceSameTabNavigation,\n autoFollowNewPage,\n });\n\n const commonAgentOpts = {\n testId: reportTag,\n reportFileName: reportTag,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: true,\n ...sharedAgentOptions,\n ...opts,\n };\n\n const agent = autoFollowNewPage\n ? new PlaywrightBrowserAgent(page.context(), page, {\n ...commonAgentOpts,\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n })\n : new PlaywrightAgent(page, {\n ...commonAgentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n pageAgentMap[idForPage] = agent;\n const records = getAgentRecordsForTest(testInfo);\n const record: AgentRecord = { agent };\n records.set(idForPage, record);\n\n page.on('close', async () => {\n debugPage('page closed');\n try {\n await finalizeAgentRecord(record);\n } finally {\n delete pageAgentMap[idForPage];\n }\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiAction'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: unknown, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n type AgentMethod = (...methodArgs: any[]) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod).bind(\n agent,\n )(taskPrompt, ...args);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n return {\n _midsceneFinalizeReports: [\n // biome-ignore lint/correctness/noEmptyPattern: Playwright fixture callbacks must use object destructuring for the first parameter even when no fixtures are consumed.\n async ({}: Record<string, unknown>, use: any, testInfo: TestInfo) => {\n await use();\n\n const records = testAgentRecords.get(testInfo.testId);\n if (!records || records.size === 0) {\n return;\n }\n\n const reportPaths = (\n await Promise.all(\n Array.from(records.values()).map((record) =>\n finalizeAgentRecord(record),\n ),\n )\n ).filter((reportPath): reportPath is string => Boolean(reportPath));\n\n if (reportPaths.length > 0) {\n setReportAnnotation(testInfo, reportPaths);\n }\n\n testAgentRecords.delete(testInfo.testId);\n },\n { auto: true },\n ],\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: WebPageAgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n /**\n * @deprecated Use {@link PlaywrightAiFixture.aiAct} instead.\n */\n aiAction: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAction',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: OriginPlaywrightPage,\n opts?: WebPageAgentOpt,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(...args: Parameters<PageAgent['ai']>) => Promise<T>;\n aiAct: (\n ...args: Parameters<PageAgent['aiAct']>\n ) => ReturnType<PageAgent['aiAct']>;\n /**\n * @deprecated Use {@link PlayWrightAiFixtureType.aiAct} instead.\n */\n aiAction: (\n ...args: Parameters<PageAgent['aiAction']>\n ) => ReturnType<PageAgent['aiAction']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","PlaywrightAiFixture","options","forceSameTabNavigation","autoFollowNewPage","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","sharedAgentOptions","processTestCacheConfig","id","processCacheConfig","pageAgentMap","testAgentRecords","Map","getAgentRecordsForTest","records","setReportAnnotation","reportPaths","item","reportPath","finalizeAgentRecord","record","undefined","createOrReuseAgentForPage","page","opts","idForPage","uuid","file","title","cacheConfig","reportTag","buildPlaywrightReportTag","Error","runtimeOptions","resolveBrowserAgentRuntimeOptions","commonAgentOpts","agent","PlaywrightBrowserAgent","PlaywrightAgent","generateAiFunction","use","aiActionType","taskPrompt","args","Promise","resolve","reject","test","JSON","result","error","Array","Boolean","propsPage","finalCacheConfig","userCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playwright/ai-fixture.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/ai-fixture.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { resolveBrowserAgentRuntimeOptions } from '@/common/browser-agent';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { replaceIllegalPathCharsAndSpace, uuid } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nimport {\n PlaywrightAgent,\n PlaywrightBrowserAgent,\n type PlaywrightWebPage,\n} from './agent';\nimport { buildPlaywrightReportTag } from './report-filename';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\ntype AgentRecord = {\n agent: PageAgent<PlaywrightWebPage>;\n finalizePromise?: Promise<string | undefined>;\n finalReportPath?: string;\n};\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport type PlaywrightAiFixtureOptions = Omit<\n WebPageAgentOpt,\n | 'testId'\n | 'cacheId'\n | 'groupName'\n | 'groupDescription'\n | 'reportFileName'\n | 'cache'\n> & {\n autoFollowNewPage?: boolean;\n cache?: PlaywrightCache;\n};\n\nexport const PlaywrightAiFixture = (options?: PlaywrightAiFixtureOptions) => {\n const {\n forceSameTabNavigation = true,\n autoFollowNewPage = false,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n ...sharedAgentOptions\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const testAgentRecords = new Map<string, Map<string, AgentRecord>>();\n\n const getAgentRecordsForTest = (testInfo: TestInfo) => {\n let records = testAgentRecords.get(testInfo.testId);\n if (!records) {\n records = new Map<string, AgentRecord>();\n testAgentRecords.set(testInfo.testId, records);\n }\n return records;\n };\n\n const setReportAnnotation = (testInfo: TestInfo, reportPaths: string[]) => {\n testInfo.annotations = testInfo.annotations.filter((item) => {\n return item.type !== midsceneDumpAnnotationId;\n });\n\n for (const reportPath of reportPaths) {\n testInfo.annotations.push({\n type: midsceneDumpAnnotationId,\n description: reportPath,\n });\n }\n };\n\n const finalizeAgentRecord = async (\n record: AgentRecord,\n ): Promise<string | undefined> => {\n if (!record.finalizePromise) {\n record.finalizePromise = (async () => {\n await record.agent.destroy();\n const reportPath = record.agent.reportFile || undefined;\n record.finalReportPath = reportPath;\n return reportPath;\n })();\n }\n\n return await record.finalizePromise;\n };\n\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n // `replaceIllegalPathCharsAndSpace` intentionally preserves `/` and `\\`\n // so groupName/groupDescription can still carry hierarchy. But\n // ReportGenerator rejects path separators in the file name, so the\n // report tag builder strips them without changing the group metadata.\n const reportTag = buildPlaywrightReportTag(title, idForPage);\n\n if (autoFollowNewPage && forceSameTabNavigation === true) {\n throw new Error(\n '[midscene] autoFollowNewPage cannot be used with forceSameTabNavigation: true.',\n );\n }\n\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightAiFixture',\n pageScope: autoFollowNewPage ? 'browser' : 'page',\n forceSameTabNavigation: autoFollowNewPage\n ? undefined\n : forceSameTabNavigation,\n autoFollowNewPage,\n });\n\n const commonAgentOpts = {\n testId: reportTag,\n reportFileName: reportTag,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: true,\n ...sharedAgentOptions,\n ...opts,\n };\n\n const agent = autoFollowNewPage\n ? new PlaywrightBrowserAgent(page.context(), page, {\n ...commonAgentOpts,\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n })\n : new PlaywrightAgent(page, {\n ...commonAgentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n pageAgentMap[idForPage] = agent;\n const records = getAgentRecordsForTest(testInfo);\n const record: AgentRecord = { agent };\n records.set(idForPage, record);\n\n page.on('close', async () => {\n debugPage('page closed');\n try {\n await finalizeAgentRecord(record);\n } finally {\n delete pageAgentMap[idForPage];\n }\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiAction'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: unknown, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n type AgentMethod = (...methodArgs: any[]) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod).bind(\n agent,\n )(taskPrompt, ...args);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n return {\n _midsceneFinalizeReports: [\n // biome-ignore lint/correctness/noEmptyPattern: Playwright fixture callbacks must use object destructuring for the first parameter even when no fixtures are consumed.\n async ({}: Record<string, unknown>, use: any, testInfo: TestInfo) => {\n await use();\n\n const records = testAgentRecords.get(testInfo.testId);\n if (!records || records.size === 0) {\n return;\n }\n\n const reportPaths = (\n await Promise.all(\n Array.from(records.values()).map((record) =>\n finalizeAgentRecord(record),\n ),\n )\n ).filter((reportPath): reportPath is string => Boolean(reportPath));\n\n if (reportPaths.length > 0) {\n setReportAnnotation(testInfo, reportPaths);\n }\n\n testAgentRecords.delete(testInfo.testId);\n },\n { auto: true },\n ],\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: WebPageAgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n /**\n * @deprecated Use {@link PlaywrightAiFixture.aiAct} instead.\n */\n aiAction: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAction',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: OriginPlaywrightPage,\n opts?: WebPageAgentOpt,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(...args: Parameters<PageAgent['ai']>) => Promise<T>;\n aiAct: (\n ...args: Parameters<PageAgent['aiAct']>\n ) => ReturnType<PageAgent['aiAct']>;\n /**\n * @deprecated Use {@link PlayWrightAiFixtureType.aiAct} instead.\n */\n aiAction: (\n ...args: Parameters<PageAgent['aiAction']>\n ) => ReturnType<PageAgent['aiAction']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","PlaywrightAiFixture","options","forceSameTabNavigation","autoFollowNewPage","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","sharedAgentOptions","processTestCacheConfig","id","processCacheConfig","pageAgentMap","testAgentRecords","Map","getAgentRecordsForTest","records","setReportAnnotation","reportPaths","item","reportPath","finalizeAgentRecord","record","undefined","createOrReuseAgentForPage","page","opts","idForPage","uuid","file","title","cacheConfig","reportTag","buildPlaywrightReportTag","Error","runtimeOptions","resolveBrowserAgentRuntimeOptions","commonAgentOpts","agent","PlaywrightBrowserAgent","PlaywrightAgent","generateAiFunction","use","aiActionType","taskPrompt","args","Promise","resolve","reject","test","JSON","result","error","Array","Boolean","propsPage","finalCacheConfig","userCache"],"mappings":"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"}
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
2
|
+
var __webpack_modules__ = {
|
|
3
|
+
"./agent" (module) {
|
|
4
|
+
module.exports = require("./agent.js");
|
|
5
|
+
},
|
|
6
|
+
"./ai-fixture" (module) {
|
|
7
|
+
module.exports = require("./ai-fixture.js");
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var __webpack_module_cache__ = {};
|
|
11
|
+
function __webpack_require__(moduleId) {
|
|
12
|
+
var cachedModule = __webpack_module_cache__[moduleId];
|
|
13
|
+
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
14
|
+
var module = __webpack_module_cache__[moduleId] = {
|
|
15
|
+
exports: {}
|
|
16
|
+
};
|
|
17
|
+
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
18
|
+
return module.exports;
|
|
19
|
+
}
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.n = (module)=>{
|
|
22
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
23
|
+
__webpack_require__.d(getter, {
|
|
24
|
+
a: getter
|
|
25
|
+
});
|
|
26
|
+
return getter;
|
|
27
|
+
};
|
|
28
|
+
})();
|
|
3
29
|
(()=>{
|
|
4
30
|
__webpack_require__.d = (exports1, definition)=>{
|
|
5
31
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
@@ -22,33 +48,23 @@ var __webpack_require__ = {};
|
|
|
22
48
|
};
|
|
23
49
|
})();
|
|
24
50
|
var __webpack_exports__ = {};
|
|
25
|
-
|
|
26
|
-
__webpack_require__.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
exports.PlaywrightAgent = __webpack_exports__.PlaywrightAgent;
|
|
51
|
+
(()=>{
|
|
52
|
+
__webpack_require__.r(__webpack_exports__);
|
|
53
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
54
|
+
PlaywrightAiFixture: ()=>_ai_fixture__rspack_import_0.PlaywrightAiFixture
|
|
55
|
+
});
|
|
56
|
+
var _ai_fixture__rspack_import_0 = __webpack_require__("./ai-fixture");
|
|
57
|
+
var _agent__rspack_import_1 = __webpack_require__("./agent");
|
|
58
|
+
var __rspack_reexport = {};
|
|
59
|
+
for(const __rspack_import_key in _agent__rspack_import_1)if ([
|
|
60
|
+
"PlaywrightAiFixture",
|
|
61
|
+
"default"
|
|
62
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_agent__rspack_import_1[__rspack_import_key];
|
|
63
|
+
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
64
|
+
})();
|
|
40
65
|
exports.PlaywrightAiFixture = __webpack_exports__.PlaywrightAiFixture;
|
|
41
|
-
exports.PlaywrightBrowserAgent = __webpack_exports__.PlaywrightBrowserAgent;
|
|
42
|
-
exports.PlaywrightPageAgent = __webpack_exports__.PlaywrightPageAgent;
|
|
43
|
-
exports.PlaywrightWebPage = __webpack_exports__.PlaywrightWebPage;
|
|
44
|
-
exports.overrideAIConfig = __webpack_exports__.overrideAIConfig;
|
|
45
66
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
46
|
-
"
|
|
47
|
-
"PlaywrightAiFixture",
|
|
48
|
-
"PlaywrightBrowserAgent",
|
|
49
|
-
"PlaywrightPageAgent",
|
|
50
|
-
"PlaywrightWebPage",
|
|
51
|
-
"overrideAIConfig"
|
|
67
|
+
"PlaywrightAiFixture"
|
|
52
68
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
53
69
|
Object.defineProperty(exports, '__esModule', {
|
|
54
70
|
value: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/index.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"playwright/index.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol"],"mappings":";;;;;;;;;;;;;;;;;;;;IACAA,oBAAoB,CAAC,GAAG,CAACC;QACxB,IAAIC,SAASD,UAAUA,OAAO,UAAU,GACvC,IAAOA,MAAM,CAAC,UAAU,GACxB,IAAOA;QACRD,oBAAoB,CAAC,CAACE,QAAQ;YAAE,GAAGA;QAAO;QAC1C,OAAOA;IACR;;;ICPAF,oBAAoB,CAAC,GAAG,CAAC,UAASG;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGH,oBAAoB,CAAC,CAACG,YAAYC,QAAQ,CAACJ,oBAAoB,CAAC,CAAC,UAASI,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAJ,oBAAoB,CAAC,GAAG,CAACM,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFP,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOQ,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D"}
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { overrideAIConfig } from '@midscene/shared/env';
|
|
2
|
+
export { WebPage as PlaywrightWebPage } from './page';
|
|
3
|
+
export type { WebPageAgentOpt } from '../web-element';
|
|
4
|
+
export { PlaywrightPageAgent, PlaywrightAgent } from './page-agent';
|
|
5
|
+
export { PlaywrightBrowserAgent, type PlaywrightBrowserAgentCreateOpt, type PlaywrightBrowserAgentOpt, } from './browser-agent';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type PlaywrightWebPage } from './index';
|
|
2
1
|
import type { WebPageAgentOpt } from '../web-element';
|
|
3
2
|
import type { Agent as PageAgent } from '@midscene/core/agent';
|
|
4
3
|
import { type TestInfo, type TestType } from '@playwright/test';
|
|
5
4
|
import type { Page as OriginPlaywrightPage } from 'playwright';
|
|
5
|
+
import { type PlaywrightWebPage } from './agent';
|
|
6
6
|
export type APITestType = Pick<TestType<any, any>, 'step'>;
|
|
7
7
|
export declare const midsceneDumpAnnotationId = "MIDSCENE_DUMP_ANNOTATION";
|
|
8
8
|
type PlaywrightCacheConfig = {
|
|
@@ -1,7 +1,3 @@
|
|
|
1
1
|
export type { PlayWrightAiFixtureType, PlaywrightAiFixtureOptions, } from './ai-fixture';
|
|
2
2
|
export { PlaywrightAiFixture } from './ai-fixture';
|
|
3
|
-
export
|
|
4
|
-
export { WebPage as PlaywrightWebPage } from './page';
|
|
5
|
-
export type { WebPageAgentOpt } from '../web-element';
|
|
6
|
-
export { PlaywrightPageAgent, PlaywrightAgent } from './page-agent';
|
|
7
|
-
export { PlaywrightBrowserAgent, type PlaywrightBrowserAgentCreateOpt, type PlaywrightBrowserAgentOpt, } from './browser-agent';
|
|
3
|
+
export * from './agent';
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"Browser use",
|
|
9
9
|
"Android use"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.12.
|
|
11
|
+
"version": "1.12.5-beta-20260907092148.0",
|
|
12
12
|
"repository": "https://github.com/web-infra-dev/midscene",
|
|
13
13
|
"homepage": "https://midscenejs.com/",
|
|
14
14
|
"main": "./dist/lib/index.js",
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
"require": "./dist/lib/playwright/index.js",
|
|
56
56
|
"import": "./dist/es/playwright/index.mjs"
|
|
57
57
|
},
|
|
58
|
+
"./playwright/agent": {
|
|
59
|
+
"types": "./dist/types/playwright/agent.d.ts",
|
|
60
|
+
"require": "./dist/lib/playwright/agent.js",
|
|
61
|
+
"import": "./dist/es/playwright/agent.mjs"
|
|
62
|
+
},
|
|
58
63
|
"./playwright-report": {
|
|
59
64
|
"types": "./dist/types/playwright/reporter/index.d.ts",
|
|
60
65
|
"require": "./dist/lib/playwright/reporter/index.js",
|
|
@@ -106,9 +111,9 @@
|
|
|
106
111
|
"socket.io": "^4.8.1",
|
|
107
112
|
"socket.io-client": "4.8.1",
|
|
108
113
|
"ws": "^8.18.1",
|
|
109
|
-
"@midscene/core": "1.12.
|
|
110
|
-
"@midscene/
|
|
111
|
-
"@midscene/
|
|
114
|
+
"@midscene/core": "1.12.5-beta-20260907092148.0",
|
|
115
|
+
"@midscene/playground": "1.12.5-beta-20260907092148.0",
|
|
116
|
+
"@midscene/shared": "1.12.5-beta-20260907092148.0"
|
|
112
117
|
},
|
|
113
118
|
"devDependencies": {
|
|
114
119
|
"@playwright/test": "^1.45.0",
|