@midscene/web 1.12.6-beta-20260910093036.0 → 1.12.6
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 +2 -2
- package/dist/es/bridge-mode/io-server.mjs.map +1 -1
- package/dist/es/bridge-mode/page-browser-side.mjs +1 -1
- package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -1
- package/dist/es/cli.mjs +1 -1
- package/dist/es/playwright/browser-agent.mjs +9 -1
- package/dist/es/playwright/browser-agent.mjs.map +1 -1
- package/dist/es/playwright/page-agent.mjs +19 -1
- package/dist/es/playwright/page-agent.mjs.map +1 -1
- package/dist/es/playwright/test-runner/agent-nodes.mjs +13 -0
- package/dist/es/playwright/test-runner/agent-nodes.mjs.map +1 -0
- package/dist/es/playwright/test-runner/clear-cookies.mjs +45 -0
- package/dist/es/playwright/test-runner/clear-cookies.mjs.map +1 -0
- package/dist/es/playwright/test-runner/cookies.mjs +162 -0
- package/dist/es/playwright/test-runner/cookies.mjs.map +1 -0
- package/dist/es/playwright/test-runner/goto-url.mjs +60 -0
- package/dist/es/playwright/test-runner/goto-url.mjs.map +1 -0
- package/dist/es/playwright/test-runner/index.mjs +6 -0
- package/dist/es/playwright/test-runner/types.mjs +0 -0
- package/dist/es/playwright/test-runner/utils.mjs +28 -0
- package/dist/es/playwright/test-runner/utils.mjs.map +1 -0
- package/dist/es/playwright/test-runner/viewport.mjs +35 -0
- package/dist/es/playwright/test-runner/viewport.mjs.map +1 -0
- package/dist/lib/bridge-mode/io-client.js +1 -1
- package/dist/lib/bridge-mode/io-server.js +2 -2
- package/dist/lib/bridge-mode/io-server.js.map +1 -1
- package/dist/lib/bridge-mode/page-browser-side.js +1 -1
- package/dist/lib/bridge-mode/page-browser-side.js.map +1 -1
- package/dist/lib/cli.js +1 -1
- package/dist/lib/playwright/browser-agent.js +9 -1
- package/dist/lib/playwright/browser-agent.js.map +1 -1
- package/dist/lib/playwright/page-agent.js +19 -1
- package/dist/lib/playwright/page-agent.js.map +1 -1
- package/dist/lib/playwright/test-runner/agent-nodes.js +47 -0
- package/dist/lib/playwright/test-runner/agent-nodes.js.map +1 -0
- package/dist/lib/playwright/test-runner/clear-cookies.js +82 -0
- package/dist/lib/playwright/test-runner/clear-cookies.js.map +1 -0
- package/dist/lib/playwright/test-runner/cookies.js +199 -0
- package/dist/lib/playwright/test-runner/cookies.js.map +1 -0
- package/dist/lib/playwright/test-runner/goto-url.js +97 -0
- package/dist/lib/playwright/test-runner/goto-url.js.map +1 -0
- package/dist/lib/playwright/test-runner/index.js +54 -0
- package/dist/lib/playwright/test-runner/index.js.map +1 -0
- package/dist/lib/playwright/test-runner/types.js +20 -0
- package/dist/lib/playwright/test-runner/types.js.map +1 -0
- package/dist/lib/playwright/test-runner/utils.js +68 -0
- package/dist/lib/playwright/test-runner/utils.js.map +1 -0
- package/dist/lib/playwright/test-runner/viewport.js +72 -0
- package/dist/lib/playwright/test-runner/viewport.js.map +1 -0
- package/dist/types/playwright/agent.d.ts +2 -0
- package/dist/types/playwright/browser-agent.d.ts +5 -0
- package/dist/types/playwright/page-agent.d.ts +8 -1
- package/dist/types/playwright/test-runner/agent-nodes.d.ts +2 -0
- package/dist/types/playwright/test-runner/clear-cookies.d.ts +16 -0
- package/dist/types/playwright/test-runner/cookies.d.ts +32 -0
- package/dist/types/playwright/test-runner/goto-url.d.ts +25 -0
- package/dist/types/playwright/test-runner/index.d.ts +10 -0
- package/dist/types/playwright/test-runner/types.d.ts +41 -0
- package/dist/types/playwright/test-runner/utils.d.ts +4 -0
- package/dist/types/playwright/test-runner/viewport.d.ts +13 -0
- package/package.json +10 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright/test-runner/cookies.mjs","sources":["../../../../src/playwright/test-runner/cookies.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { AgentTestRunnerNodeDefinition } from '@midscene/core/agent';\nimport { z } from 'zod/v4';\nimport type {\n PlaywrightCookie,\n PlaywrightNodeContext,\n PlaywrightTestRunnerAgent,\n PlaywrightTestRunnerOptions,\n} from './types';\nimport { requirePlaywrightAgent, resolveWebUrl, throwIfAborted } from './utils';\n\ntype CookieSourceReference =\n | { kind: 'env'; name: string }\n | { kind: 'profile'; name: string }\n | { kind: 'storage-state'; name: string };\n\ninterface ResolvedCookieSource {\n reference: CookieSourceReference;\n cookies: unknown;\n}\n\n/** Input schema for the Playwright setCookies Node. */\nexport const setCookiesInputSchema = z\n .strictObject({\n cookiesEnv: z\n .string()\n .regex(/^[A-Za-z_][A-Za-z0-9_]*$/)\n .optional()\n .describe(\n 'The environment variable containing a Cookie header, cookie array JSON, or Playwright storage-state JSON.',\n ),\n profile: z\n .string()\n .regex(/\\S/)\n .optional()\n .describe('A cookie profile resolved by getCookieProfile().'),\n storageStatePath: z\n .string()\n .regex(/\\S/)\n .optional()\n .describe('A path to a Playwright storage-state JSON file.'),\n url: z\n .string()\n .url()\n .optional()\n .describe(\n 'The HTTP(S) URL used to scope Cookie-header values or cookies without domain/path.',\n ),\n })\n .superRefine((input, ctx) => {\n const sourceCount = [\n input.cookiesEnv,\n input.profile,\n input.storageStatePath,\n ].filter((value) => value !== undefined).length;\n if (sourceCount !== 1) {\n ctx.addIssue({\n code: 'custom',\n message:\n 'exactly one of cookiesEnv, profile, and storageStatePath is required',\n });\n }\n });\n\n/** Validated input accepted by the Playwright setCookies Node. */\nexport type SetCookiesNodeInput = z.infer<typeof setCookiesInputSchema>;\n\n/** Secret-free metadata returned by the Playwright setCookies Node. */\nexport interface SetCookiesNodeResult {\n /** Kind of configured reference used to resolve the cookies. */\n source: CookieSourceReference['kind'];\n /** Environment variable, profile, or storage-state reference name. */\n sourceName: string;\n /** Number of cookies passed to the BrowserContext. */\n count: number;\n}\n\nconst toCookieSourceReference = (\n input: SetCookiesNodeInput,\n): CookieSourceReference => {\n if (input.cookiesEnv !== undefined) {\n return { kind: 'env', name: input.cookiesEnv };\n }\n if (input.profile !== undefined) {\n return { kind: 'profile', name: input.profile };\n }\n if (input.storageStatePath !== undefined) {\n return { kind: 'storage-state', name: input.storageStatePath };\n }\n throw new TypeError('setCookies requires one cookie source.');\n};\n\nconst parseCookieHeader = (raw: string, url: string | undefined) => {\n if (url === undefined) {\n throw new TypeError(\n 'setCookies.url is required when cookiesEnv contains a Cookie header.',\n );\n }\n const segments = raw\n .split(';')\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (segments.length === 0) {\n throw new TypeError('setCookies Cookie header contains no cookies.');\n }\n return segments.map((segment) => {\n const separator = segment.indexOf('=');\n if (separator <= 0) {\n throw new TypeError(\n 'setCookies Cookie header contains an invalid name/value pair.',\n );\n }\n const name = segment.slice(0, separator).trim();\n const value = segment.slice(separator + 1).trim();\n if (!name) {\n throw new TypeError('setCookies Cookie header contains an empty name.');\n }\n return { name, value, url } satisfies PlaywrightCookie;\n });\n};\n\nconst parseCookieJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw);\n } catch {\n throw new TypeError(\n 'setCookies source must contain valid JSON or a Cookie header.',\n );\n }\n};\n\nconst resolveCookieSource = async (\n reference: CookieSourceReference,\n defaultUrl: string | undefined,\n ctx: PlaywrightNodeContext<PlaywrightTestRunnerAgent>,\n options: PlaywrightTestRunnerOptions,\n): Promise<ResolvedCookieSource> => {\n switch (reference.kind) {\n case 'env': {\n let env: Readonly<Record<string, string | undefined>>;\n try {\n env = (await options.getEnv?.(ctx)) ?? process.env;\n } catch {\n throw new Error(\n `Failed to resolve cookie environment ${reference.name}; the original error was redacted because it may contain cookie values.`,\n );\n }\n const raw = env[reference.name];\n if (!raw?.trim()) {\n throw new Error(\n `Environment variable ${reference.name} is missing or blank.`,\n );\n }\n const trimmed = raw.trim();\n return {\n reference,\n cookies: /^[\\[{]/.test(trimmed)\n ? parseCookieJson(trimmed)\n : parseCookieHeader(trimmed, defaultUrl),\n };\n }\n case 'profile': {\n if (!options.getCookieProfile) {\n throw new Error(\n 'setCookies.profile requires a configured getCookieProfile callback.',\n );\n }\n try {\n return {\n reference,\n cookies: await options.getCookieProfile({\n profile: reference.name,\n context: ctx.context,\n signal: ctx.signal,\n }),\n };\n } catch {\n throw new Error(\n `Failed to resolve cookie profile ${reference.name}; the original error was redacted because it may contain cookie values.`,\n );\n }\n }\n case 'storage-state': {\n const path = options.resolveStorageStatePath\n ? await options.resolveStorageStatePath(reference.name, ctx)\n : resolve(process.cwd(), reference.name);\n return {\n reference,\n cookies: parseCookieJson(await readFile(path, 'utf8')),\n };\n }\n }\n};\n\nconst normalizeCookie = (\n candidate: unknown,\n defaultUrl: string | undefined,\n index: number,\n): PlaywrightCookie => {\n if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {\n throw new TypeError(\n `setCookies cookie at index ${index} must be an object.`,\n );\n }\n const cookie = candidate as Record<string, unknown>;\n if (typeof cookie.name !== 'string' || !cookie.name.trim()) {\n throw new TypeError(\n `setCookies cookie at index ${index} requires a non-blank name.`,\n );\n }\n if (typeof cookie.value !== 'string') {\n throw new TypeError(\n `setCookies cookie at index ${index} requires a string value.`,\n );\n }\n\n const normalized = { ...cookie } as unknown as PlaywrightCookie;\n const hasUrl = typeof cookie.url === 'string' && cookie.url.length > 0;\n const hasDomain =\n typeof cookie.domain === 'string' && cookie.domain.length > 0;\n if (!hasUrl && !hasDomain) {\n if (defaultUrl === undefined) {\n throw new TypeError(\n `setCookies cookie at index ${index} requires url or domain, or setCookies.url must be provided.`,\n );\n }\n normalized.url = defaultUrl;\n }\n if (hasDomain && typeof cookie.path !== 'string') {\n normalized.path = '/';\n }\n return normalized;\n};\n\nconst normalizeCookies = (\n source: unknown,\n defaultUrl: string | undefined,\n): PlaywrightCookie[] => {\n const candidates = Array.isArray(source)\n ? source\n : source &&\n typeof source === 'object' &&\n Array.isArray((source as { cookies?: unknown }).cookies)\n ? (source as { cookies: unknown[] }).cookies\n : undefined;\n if (!candidates) {\n throw new TypeError(\n 'setCookies JSON must be a cookie array or an object with a cookies array.',\n );\n }\n if (candidates.length === 0) {\n throw new TypeError('setCookies source contains no cookies.');\n }\n return candidates.map((cookie, index) =>\n normalizeCookie(cookie, defaultUrl, index),\n );\n};\n\nexport const setCookiesNode: AgentTestRunnerNodeDefinition<\n z.output<typeof setCookiesInputSchema>,\n SetCookiesNodeResult\n> = {\n name: 'setCookies',\n title: 'Set browser cookies',\n description:\n 'Load cookies from an environment variable, configured profile, or Playwright storage-state file without persisting cookie values in workflow input or output.',\n stringInputKey: false,\n inputSchema: setCookiesInputSchema,\n async execute(agent, input, executionContext) {\n const ctx = {\n ...executionContext,\n input,\n context: requirePlaywrightAgent(agent),\n };\n throwIfAborted(ctx.signal, 'setCookies');\n const page = ctx.context.interface.underlyingPage;\n const defaultUrl =\n ctx.input.url === undefined\n ? undefined\n : resolveWebUrl(ctx.input.url, undefined, 'setCookies.url');\n const reference = toCookieSourceReference(ctx.input);\n const resolved = await resolveCookieSource(\n reference,\n defaultUrl,\n ctx,\n ctx.context.testRunner ?? {},\n );\n const cookies = normalizeCookies(resolved.cookies, defaultUrl);\n\n throwIfAborted(ctx.signal, 'setCookies');\n try {\n await page.context().addCookies(cookies);\n } catch {\n throw new Error(\n `Failed to set ${cookies.length} browser cookie(s); the browser error was redacted because it may contain cookie values.`,\n );\n }\n\n const result: SetCookiesNodeResult = {\n source: resolved.reference.kind,\n sourceName: resolved.reference.name,\n count: cookies.length,\n };\n return {\n summary: `Set ${result.count} browser cookie(s) from ${result.source} source ${result.sourceName}`,\n data: result,\n };\n },\n};\n"],"names":["setCookiesInputSchema","z","input","ctx","sourceCount","value","undefined","toCookieSourceReference","TypeError","parseCookieHeader","raw","url","segments","segment","Boolean","separator","name","parseCookieJson","JSON","resolveCookieSource","reference","defaultUrl","options","env","process","Error","trimmed","path","resolve","readFile","normalizeCookie","candidate","index","Array","cookie","normalized","hasUrl","hasDomain","normalizeCookies","source","candidates","setCookiesNode","agent","executionContext","requirePlaywrightAgent","throwIfAborted","page","resolveWebUrl","resolved","cookies","result"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { requirePlaywrightAgent, resolveWebUrl, throwIfAborted } from "./utils.mjs";
|
|
3
|
+
const DEFAULT_NAVIGATION_TIMEOUT_MS = 60000;
|
|
4
|
+
const DEFAULT_WAIT_UNTIL = 'domcontentloaded';
|
|
5
|
+
const gotoUrlInputSchema = z.strictObject({
|
|
6
|
+
url: z.string().min(1).describe('An absolute HTTP(S) URL or a path relative to Playwright baseURL or the current page URL.'),
|
|
7
|
+
waitUntil: z["enum"]([
|
|
8
|
+
'commit',
|
|
9
|
+
'domcontentloaded',
|
|
10
|
+
'load',
|
|
11
|
+
'networkidle'
|
|
12
|
+
]).default(DEFAULT_WAIT_UNTIL).describe('The Playwright navigation lifecycle event to wait for.'),
|
|
13
|
+
timeoutMs: z.number().positive().default(DEFAULT_NAVIGATION_TIMEOUT_MS).describe('The navigation timeout in milliseconds.')
|
|
14
|
+
});
|
|
15
|
+
const resolveNavigationUrl = (target, page)=>{
|
|
16
|
+
const trimmed = target.trim();
|
|
17
|
+
if (!trimmed) throw new TypeError('gotoUrl.url must not be blank.');
|
|
18
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(trimmed)) return resolveWebUrl(trimmed, void 0, 'gotoUrl.url');
|
|
19
|
+
const context = page.context();
|
|
20
|
+
const configuredBase = context._options?.baseURL;
|
|
21
|
+
if (void 0 !== configuredBase) return resolveWebUrl(trimmed, configuredBase, 'gotoUrl.url');
|
|
22
|
+
const currentUrl = page.url();
|
|
23
|
+
if (!/^https?:\/\//i.test(currentUrl)) throw new TypeError(`gotoUrl.url cannot resolve a relative URL from ${JSON.stringify(currentUrl)}. Use an absolute HTTP(S) URL for the first navigation or configure Playwright baseURL.`);
|
|
24
|
+
return resolveWebUrl(trimmed, currentUrl, 'gotoUrl.url');
|
|
25
|
+
};
|
|
26
|
+
const gotoUrlNode = {
|
|
27
|
+
name: 'gotoUrl',
|
|
28
|
+
title: 'Open a Web URL',
|
|
29
|
+
description: 'Navigate the current Playwright Page to an absolute HTTP(S) URL or a path relative to Playwright baseURL or the current page URL.',
|
|
30
|
+
stringInputKey: 'url',
|
|
31
|
+
inputSchema: gotoUrlInputSchema,
|
|
32
|
+
async execute (agent, input, executionContext) {
|
|
33
|
+
const ctx = {
|
|
34
|
+
...executionContext,
|
|
35
|
+
input,
|
|
36
|
+
context: requirePlaywrightAgent(agent)
|
|
37
|
+
};
|
|
38
|
+
throwIfAborted(ctx.signal, 'gotoUrl');
|
|
39
|
+
const page = ctx.context.interface.underlyingPage;
|
|
40
|
+
const url = resolveNavigationUrl(ctx.input.url, page);
|
|
41
|
+
const response = await page.goto(url, {
|
|
42
|
+
waitUntil: ctx.input.waitUntil,
|
|
43
|
+
timeout: ctx.input.timeoutMs
|
|
44
|
+
});
|
|
45
|
+
throwIfAborted(ctx.signal, 'gotoUrl');
|
|
46
|
+
const status = response?.status() ?? null;
|
|
47
|
+
const result = {
|
|
48
|
+
url: page.url(),
|
|
49
|
+
status,
|
|
50
|
+
title: await page.title()
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
summary: `Navigated to ${result.url}`,
|
|
54
|
+
data: result
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
export { gotoUrlInputSchema, gotoUrlNode };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=goto-url.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright/test-runner/goto-url.mjs","sources":["../../../../src/playwright/test-runner/goto-url.ts"],"sourcesContent":["import type { AgentTestRunnerNodeDefinition } from '@midscene/core/agent';\nimport type { BrowserContext, Page } from 'playwright';\nimport { z } from 'zod/v4';\nimport { requirePlaywrightAgent, resolveWebUrl, throwIfAborted } from './utils';\n\nconst DEFAULT_NAVIGATION_TIMEOUT_MS = 60_000;\nconst DEFAULT_WAIT_UNTIL = 'domcontentloaded' as const;\n\n/** Input schema for the Playwright gotoUrl Node. */\nexport const gotoUrlInputSchema = z.strictObject({\n url: z\n .string()\n .min(1)\n .describe(\n 'An absolute HTTP(S) URL or a path relative to Playwright baseURL or the current page URL.',\n ),\n waitUntil: z\n .enum(['commit', 'domcontentloaded', 'load', 'networkidle'])\n .default(DEFAULT_WAIT_UNTIL)\n .describe('The Playwright navigation lifecycle event to wait for.'),\n timeoutMs: z\n .number()\n .positive()\n .default(DEFAULT_NAVIGATION_TIMEOUT_MS)\n .describe('The navigation timeout in milliseconds.'),\n});\n\n/** Validated input accepted by the Playwright gotoUrl Node. */\nexport type GotoUrlNodeInput = z.infer<typeof gotoUrlInputSchema>;\n\n/** Structured navigation details returned by the Playwright gotoUrl Node. */\nexport interface GotoUrlNodeResult {\n /** Final Page URL after navigation and redirects. */\n url: string;\n /** Main-resource HTTP status, or null when Playwright returns no response. */\n status: number | null;\n /** Document title after navigation completes. */\n title: string;\n}\n\nconst resolveNavigationUrl = (target: string, page: Page): string => {\n const trimmed = target.trim();\n if (!trimmed) throw new TypeError('gotoUrl.url must not be blank.');\n // Absolute URLs do not depend on either configured or current-page bases.\n if (/^[a-z][a-z\\d+.-]*:/i.test(trimmed)) {\n return resolveWebUrl(trimmed, undefined, 'gotoUrl.url');\n }\n\n // Playwright has no public baseURL getter. Its BrowserContext stores the\n // configured value in _options; keep this internal access localized here.\n const context = page.context() as BrowserContext & {\n _options?: { baseURL?: string };\n };\n const configuredBase = context._options?.baseURL;\n if (configuredBase !== undefined) {\n return resolveWebUrl(trimmed, configuredBase, 'gotoUrl.url');\n }\n\n const currentUrl = page.url();\n if (!/^https?:\\/\\//i.test(currentUrl)) {\n throw new TypeError(\n `gotoUrl.url cannot resolve a relative URL from ${JSON.stringify(currentUrl)}. Use an absolute HTTP(S) URL for the first navigation or configure Playwright baseURL.`,\n );\n }\n return resolveWebUrl(trimmed, currentUrl, 'gotoUrl.url');\n};\n\nexport const gotoUrlNode: AgentTestRunnerNodeDefinition<\n z.output<typeof gotoUrlInputSchema>,\n GotoUrlNodeResult\n> = {\n name: 'gotoUrl',\n title: 'Open a Web URL',\n description:\n 'Navigate the current Playwright Page to an absolute HTTP(S) URL or a path relative to Playwright baseURL or the current page URL.',\n stringInputKey: 'url',\n inputSchema: gotoUrlInputSchema,\n async execute(agent, input, executionContext) {\n const ctx = {\n ...executionContext,\n input,\n context: requirePlaywrightAgent(agent),\n };\n throwIfAborted(ctx.signal, 'gotoUrl');\n const page = ctx.context.interface.underlyingPage;\n const url = resolveNavigationUrl(ctx.input.url, page);\n const response = await page.goto(url, {\n waitUntil: ctx.input.waitUntil,\n timeout: ctx.input.timeoutMs,\n });\n throwIfAborted(ctx.signal, 'gotoUrl');\n const status = response?.status() ?? null;\n const result = {\n url: page.url(),\n status,\n title: await page.title(),\n };\n return { summary: `Navigated to ${result.url}`, data: result };\n },\n};\n"],"names":["DEFAULT_NAVIGATION_TIMEOUT_MS","DEFAULT_WAIT_UNTIL","gotoUrlInputSchema","z","resolveNavigationUrl","target","page","trimmed","TypeError","resolveWebUrl","undefined","context","configuredBase","currentUrl","JSON","gotoUrlNode","agent","input","executionContext","ctx","requirePlaywrightAgent","throwIfAborted","url","response","status","result"],"mappings":";;AAKA,MAAMA,gCAAgC;AACtC,MAAMC,qBAAqB;AAGpB,MAAMC,qBAAqBC,EAAE,YAAY,CAAC;IAC/C,KAAKA,EAAAA,MACI,GACN,GAAG,CAAC,GACJ,QAAQ,CACP;IAEJ,WAAWA,CAAC,CAADA,OACJ,CAAC;QAAC;QAAU;QAAoB;QAAQ;KAAc,EAC1D,OAAO,CAACF,oBACR,QAAQ,CAAC;IACZ,WAAWE,EAAAA,MACF,GACN,QAAQ,GACR,OAAO,CAACH,+BACR,QAAQ,CAAC;AACd;AAeA,MAAMI,uBAAuB,CAACC,QAAgBC;IAC5C,MAAMC,UAAUF,OAAO,IAAI;IAC3B,IAAI,CAACE,SAAS,MAAM,IAAIC,UAAU;IAElC,IAAI,sBAAsB,IAAI,CAACD,UAC7B,OAAOE,cAAcF,SAASG,QAAW;IAK3C,MAAMC,UAAUL,KAAK,OAAO;IAG5B,MAAMM,iBAAiBD,QAAQ,QAAQ,EAAE;IACzC,IAAIC,AAAmBF,WAAnBE,gBACF,OAAOH,cAAcF,SAASK,gBAAgB;IAGhD,MAAMC,aAAaP,KAAK,GAAG;IAC3B,IAAI,CAAC,gBAAgB,IAAI,CAACO,aACxB,MAAM,IAAIL,UACR,CAAC,+CAA+C,EAAEM,KAAK,SAAS,CAACD,YAAY,uFAAuF,CAAC;IAGzK,OAAOJ,cAAcF,SAASM,YAAY;AAC5C;AAEO,MAAME,cAGT;IACF,MAAM;IACN,OAAO;IACP,aACE;IACF,gBAAgB;IAChB,aAAab;IACb,MAAM,SAAQc,KAAK,EAAEC,KAAK,EAAEC,gBAAgB;QAC1C,MAAMC,MAAM;YACV,GAAGD,gBAAgB;YACnBD;YACA,SAASG,uBAAuBJ;QAClC;QACAK,eAAeF,IAAI,MAAM,EAAE;QAC3B,MAAMb,OAAOa,IAAI,OAAO,CAAC,SAAS,CAAC,cAAc;QACjD,MAAMG,MAAMlB,qBAAqBe,IAAI,KAAK,CAAC,GAAG,EAAEb;QAChD,MAAMiB,WAAW,MAAMjB,KAAK,IAAI,CAACgB,KAAK;YACpC,WAAWH,IAAI,KAAK,CAAC,SAAS;YAC9B,SAASA,IAAI,KAAK,CAAC,SAAS;QAC9B;QACAE,eAAeF,IAAI,MAAM,EAAE;QAC3B,MAAMK,SAASD,UAAU,YAAY;QACrC,MAAME,SAAS;YACb,KAAKnB,KAAK,GAAG;YACbkB;YACA,OAAO,MAAMlB,KAAK,KAAK;QACzB;QACA,OAAO;YAAE,SAAS,CAAC,aAAa,EAAEmB,OAAO,GAAG,EAAE;YAAE,MAAMA;QAAO;IAC/D;AACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { clearCookiesInputSchema } from "./clear-cookies.mjs";
|
|
2
|
+
import { setCookiesInputSchema } from "./cookies.mjs";
|
|
3
|
+
import { gotoUrlInputSchema } from "./goto-url.mjs";
|
|
4
|
+
import { setViewportSizeInputSchema } from "./viewport.mjs";
|
|
5
|
+
import { playwrightAgentTestRunnerNodeDefinitions } from "./agent-nodes.mjs";
|
|
6
|
+
export { clearCookiesInputSchema, gotoUrlInputSchema, playwrightAgentTestRunnerNodeDefinitions, setCookiesInputSchema, setViewportSizeInputSchema };
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const SUPPORTED_WEB_PROTOCOLS = new Set([
|
|
2
|
+
'http:',
|
|
3
|
+
'https:'
|
|
4
|
+
]);
|
|
5
|
+
const throwIfAborted = (signal, operation)=>{
|
|
6
|
+
if (signal.aborted) throw signal.reason ?? new Error(`${operation} aborted.`);
|
|
7
|
+
};
|
|
8
|
+
const resolveWebUrl = (target, baseUrl, label)=>{
|
|
9
|
+
const trimmed = target.trim();
|
|
10
|
+
if (!trimmed) throw new TypeError(`${label} must not be blank.`);
|
|
11
|
+
let resolved;
|
|
12
|
+
try {
|
|
13
|
+
resolved = void 0 === baseUrl ? new URL(trimmed) : new URL(trimmed, baseUrl);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
throw new TypeError(void 0 === baseUrl ? `${label} must be an absolute HTTP(S) URL.` : `${label} is not a valid URL.`, {
|
|
16
|
+
cause: error
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
if (!SUPPORTED_WEB_PROTOCOLS.has(resolved.protocol)) throw new TypeError(`${label} does not support the ${resolved.protocol} protocol.`);
|
|
20
|
+
return resolved.toString();
|
|
21
|
+
};
|
|
22
|
+
const requirePlaywrightAgent = (agent)=>{
|
|
23
|
+
if (!agent || 'object' != typeof agent || !('interface' in agent) || !agent.interface || 'object' != typeof agent.interface || !('underlyingPage' in agent.interface) || !agent.interface.underlyingPage) throw new TypeError('getAgent() must return a Playwright Agent with an underlying Page.');
|
|
24
|
+
return agent;
|
|
25
|
+
};
|
|
26
|
+
export { requirePlaywrightAgent, resolveWebUrl, throwIfAborted };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright/test-runner/utils.mjs","sources":["../../../../src/playwright/test-runner/utils.ts"],"sourcesContent":["import type { PlaywrightTestRunnerAgent } from './types';\n\nconst SUPPORTED_WEB_PROTOCOLS = new Set(['http:', 'https:']);\n\nexport const throwIfAborted = (signal: AbortSignal, operation: string) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error(`${operation} aborted.`);\n }\n};\n\nexport const resolveWebUrl = (\n target: string,\n baseUrl: string | undefined,\n label: string,\n): string => {\n const trimmed = target.trim();\n if (!trimmed) throw new TypeError(`${label} must not be blank.`);\n\n let resolved: URL;\n try {\n resolved =\n baseUrl === undefined ? new URL(trimmed) : new URL(trimmed, baseUrl);\n } catch (error) {\n throw new TypeError(\n baseUrl === undefined\n ? `${label} must be an absolute HTTP(S) URL.`\n : `${label} is not a valid URL.`,\n { cause: error },\n );\n }\n if (!SUPPORTED_WEB_PROTOCOLS.has(resolved.protocol)) {\n throw new TypeError(\n `${label} does not support the ${resolved.protocol} protocol.`,\n );\n }\n return resolved.toString();\n};\n\nexport const requirePlaywrightAgent = (\n agent: unknown,\n): PlaywrightTestRunnerAgent => {\n if (\n !agent ||\n typeof agent !== 'object' ||\n !('interface' in agent) ||\n !agent.interface ||\n typeof agent.interface !== 'object' ||\n !('underlyingPage' in agent.interface) ||\n !agent.interface.underlyingPage\n ) {\n throw new TypeError(\n 'getAgent() must return a Playwright Agent with an underlying Page.',\n );\n }\n return agent as PlaywrightTestRunnerAgent;\n};\n"],"names":["SUPPORTED_WEB_PROTOCOLS","Set","throwIfAborted","signal","operation","Error","resolveWebUrl","target","baseUrl","label","trimmed","TypeError","resolved","undefined","URL","error","requirePlaywrightAgent","agent"],"mappings":"AAEA,MAAMA,0BAA0B,IAAIC,IAAI;IAAC;IAAS;CAAS;AAEpD,MAAMC,iBAAiB,CAACC,QAAqBC;IAClD,IAAID,OAAO,OAAO,EAChB,MAAMA,OAAO,MAAM,IAAI,IAAIE,MAAM,GAAGD,UAAU,SAAS,CAAC;AAE5D;AAEO,MAAME,gBAAgB,CAC3BC,QACAC,SACAC;IAEA,MAAMC,UAAUH,OAAO,IAAI;IAC3B,IAAI,CAACG,SAAS,MAAM,IAAIC,UAAU,GAAGF,MAAM,mBAAmB,CAAC;IAE/D,IAAIG;IACJ,IAAI;QACFA,WACEJ,AAAYK,WAAZL,UAAwB,IAAIM,IAAIJ,WAAW,IAAII,IAAIJ,SAASF;IAChE,EAAE,OAAOO,OAAO;QACd,MAAM,IAAIJ,UACRH,AAAYK,WAAZL,UACI,GAAGC,MAAM,iCAAiC,CAAC,GAC3C,GAAGA,MAAM,oBAAoB,CAAC,EAClC;YAAE,OAAOM;QAAM;IAEnB;IACA,IAAI,CAACf,wBAAwB,GAAG,CAACY,SAAS,QAAQ,GAChD,MAAM,IAAID,UACR,GAAGF,MAAM,sBAAsB,EAAEG,SAAS,QAAQ,CAAC,UAAU,CAAC;IAGlE,OAAOA,SAAS,QAAQ;AAC1B;AAEO,MAAMI,yBAAyB,CACpCC;IAEA,IACE,CAACA,SACD,AAAiB,YAAjB,OAAOA,SACP,CAAE,gBAAeA,KAAI,KACrB,CAACA,MAAM,SAAS,IAChB,AAA2B,YAA3B,OAAOA,MAAM,SAAS,IACtB,CAAE,qBAAoBA,MAAM,SAAQ,KACpC,CAACA,MAAM,SAAS,CAAC,cAAc,EAE/B,MAAM,IAAIN,UACR;IAGJ,OAAOM;AACT"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { requirePlaywrightAgent, throwIfAborted } from "./utils.mjs";
|
|
3
|
+
const setViewportSizeInputSchema = z.strictObject({
|
|
4
|
+
width: z.number().int().positive().describe('Viewport width in CSS pixels.'),
|
|
5
|
+
height: z.number().int().positive().describe('Viewport height in CSS pixels.')
|
|
6
|
+
});
|
|
7
|
+
const setViewportSizeNode = {
|
|
8
|
+
name: 'setViewportSize',
|
|
9
|
+
title: 'Set the browser viewport size',
|
|
10
|
+
description: 'Set the current Playwright Page viewport size in CSS pixels and return the effective size.',
|
|
11
|
+
stringInputKey: false,
|
|
12
|
+
inputSchema: setViewportSizeInputSchema,
|
|
13
|
+
async execute (agent, input, executionContext) {
|
|
14
|
+
const ctx = {
|
|
15
|
+
...executionContext,
|
|
16
|
+
input,
|
|
17
|
+
context: requirePlaywrightAgent(agent)
|
|
18
|
+
};
|
|
19
|
+
throwIfAborted(ctx.signal, 'setViewportSize');
|
|
20
|
+
const page = ctx.context.interface.underlyingPage;
|
|
21
|
+
await page.setViewportSize({
|
|
22
|
+
width: ctx.input.width,
|
|
23
|
+
height: ctx.input.height
|
|
24
|
+
});
|
|
25
|
+
const viewport = page.viewportSize();
|
|
26
|
+
if (!viewport) throw new Error('Playwright did not report an effective viewport size.');
|
|
27
|
+
return {
|
|
28
|
+
summary: `Set viewport to ${viewport.width}x${viewport.height}`,
|
|
29
|
+
data: viewport
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export { setViewportSizeInputSchema, setViewportSizeNode };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=viewport.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright/test-runner/viewport.mjs","sources":["../../../../src/playwright/test-runner/viewport.ts"],"sourcesContent":["import type { AgentTestRunnerNodeDefinition } from '@midscene/core/agent';\nimport { z } from 'zod/v4';\nimport { requirePlaywrightAgent, throwIfAborted } from './utils';\n\n/** Input schema for the Playwright setViewportSize Node. */\nexport const setViewportSizeInputSchema = z.strictObject({\n width: z.number().int().positive().describe('Viewport width in CSS pixels.'),\n height: z\n .number()\n .int()\n .positive()\n .describe('Viewport height in CSS pixels.'),\n});\n\n/** Validated input accepted by the Playwright setViewportSize Node. */\nexport type SetViewportSizeNodeInput = z.infer<\n typeof setViewportSizeInputSchema\n>;\n\nexport const setViewportSizeNode: AgentTestRunnerNodeDefinition<\n z.output<typeof setViewportSizeInputSchema>,\n { width: number; height: number }\n> = {\n name: 'setViewportSize',\n title: 'Set the browser viewport size',\n description:\n 'Set the current Playwright Page viewport size in CSS pixels and return the effective size.',\n stringInputKey: false,\n inputSchema: setViewportSizeInputSchema,\n async execute(agent, input, executionContext) {\n const ctx = {\n ...executionContext,\n input,\n context: requirePlaywrightAgent(agent),\n };\n throwIfAborted(ctx.signal, 'setViewportSize');\n const page = ctx.context.interface.underlyingPage;\n await page.setViewportSize({\n width: ctx.input.width,\n height: ctx.input.height,\n });\n const viewport = page.viewportSize();\n if (!viewport) {\n throw new Error('Playwright did not report an effective viewport size.');\n }\n return {\n summary: `Set viewport to ${viewport.width}x${viewport.height}`,\n data: viewport,\n };\n },\n};\n"],"names":["setViewportSizeInputSchema","z","setViewportSizeNode","agent","input","executionContext","ctx","requirePlaywrightAgent","throwIfAborted","page","viewport","Error"],"mappings":";;AAKO,MAAMA,6BAA6BC,EAAE,YAAY,CAAC;IACvD,OAAOA,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,QAAQA,EAAAA,MACC,GACN,GAAG,GACH,QAAQ,GACR,QAAQ,CAAC;AACd;AAOO,MAAMC,sBAGT;IACF,MAAM;IACN,OAAO;IACP,aACE;IACF,gBAAgB;IAChB,aAAaF;IACb,MAAM,SAAQG,KAAK,EAAEC,KAAK,EAAEC,gBAAgB;QAC1C,MAAMC,MAAM;YACV,GAAGD,gBAAgB;YACnBD;YACA,SAASG,uBAAuBJ;QAClC;QACAK,eAAeF,IAAI,MAAM,EAAE;QAC3B,MAAMG,OAAOH,IAAI,OAAO,CAAC,SAAS,CAAC,cAAc;QACjD,MAAMG,KAAK,eAAe,CAAC;YACzB,OAAOH,IAAI,KAAK,CAAC,KAAK;YACtB,QAAQA,IAAI,KAAK,CAAC,MAAM;QAC1B;QACA,MAAMI,WAAWD,KAAK,YAAY;QAClC,IAAI,CAACC,UACH,MAAM,IAAIC,MAAM;QAElB,OAAO;YACL,SAAS,CAAC,gBAAgB,EAAED,SAAS,KAAK,CAAC,CAAC,EAAEA,SAAS,MAAM,EAAE;YAC/D,MAAMA;QACR;IACF;AACF"}
|
|
@@ -130,7 +130,7 @@ class BridgeServer {
|
|
|
130
130
|
(0, shared_utils_namespaceObject.logMsg)('one client connected');
|
|
131
131
|
this.socket = socket;
|
|
132
132
|
const clientVersion = socket.handshake.query.version;
|
|
133
|
-
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.12.6
|
|
133
|
+
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.12.6, browser-side version v${clientVersion}`);
|
|
134
134
|
socket.on(external_common_js_namespaceObject.BridgeEvent.CallResponse, (params)=>{
|
|
135
135
|
const id = params.id;
|
|
136
136
|
const response = params.response;
|
|
@@ -154,7 +154,7 @@ class BridgeServer {
|
|
|
154
154
|
setTimeout(()=>{
|
|
155
155
|
this.onConnect?.();
|
|
156
156
|
const payload = {
|
|
157
|
-
version: "1.12.6
|
|
157
|
+
version: "1.12.6"
|
|
158
158
|
};
|
|
159
159
|
socket.emit(external_common_js_namespaceObject.BridgeEvent.Connected, payload);
|
|
160
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 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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"}
|
|
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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"}
|
|
@@ -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.6
|
|
141
|
+
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.12.6`, '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 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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"}
|
|
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.6
|
|
45
|
+
version: "1.12.6",
|
|
46
46
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
47
47
|
});
|
|
48
48
|
}).catch((e)=>{
|
|
@@ -30,6 +30,7 @@ const browser_agent_js_namespaceObject = require("../common/browser-agent.js");
|
|
|
30
30
|
const browser_agent_utils_js_namespaceObject = require("../common/browser-agent-utils.js");
|
|
31
31
|
const logger_namespaceObject = require("@midscene/shared/logger");
|
|
32
32
|
const external_page_js_namespaceObject = require("./page.js");
|
|
33
|
+
const agent_nodes_js_namespaceObject = require("./test-runner/agent-nodes.js");
|
|
33
34
|
function _define_property(obj, key, value) {
|
|
34
35
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
35
36
|
value: value,
|
|
@@ -51,6 +52,12 @@ const createPlaywrightBrowserAdapter = (context)=>({
|
|
|
51
52
|
resolveNewPage: (page)=>page
|
|
52
53
|
});
|
|
53
54
|
class PlaywrightBrowserAgent extends browser_agent_js_namespaceObject.WebAgentCore {
|
|
55
|
+
static getTestRunnerNodeDefinitions() {
|
|
56
|
+
return [
|
|
57
|
+
...browser_agent_js_namespaceObject.WebAgentCore.getTestRunnerNodeDefinitions(),
|
|
58
|
+
...agent_nodes_js_namespaceObject.playwrightAgentTestRunnerNodeDefinitions
|
|
59
|
+
];
|
|
60
|
+
}
|
|
54
61
|
static async create(context, opts) {
|
|
55
62
|
const { initialPage, ...agentOpts } = opts ?? {};
|
|
56
63
|
const page = initialPage ?? context.pages()[0] ?? await context.newPage();
|
|
@@ -102,8 +109,9 @@ class PlaywrightBrowserAgent extends browser_agent_js_namespaceObject.WebAgentCo
|
|
|
102
109
|
newPageTimeout: runtimeOptions.newPageTimeout,
|
|
103
110
|
debug
|
|
104
111
|
});
|
|
105
|
-
super(webPage, agentOpts), _define_property(this, "pageManager", void 0);
|
|
112
|
+
super(webPage, agentOpts), _define_property(this, "testRunner", void 0), _define_property(this, "pageManager", void 0);
|
|
106
113
|
this.pageManager = pageManager;
|
|
114
|
+
this.testRunner = opts?.testRunner;
|
|
107
115
|
(0, browser_agent_utils_js_namespaceObject.applyForceChromeSelectRendering)(initialPage, 'playwright', forceChromeSelectRendering);
|
|
108
116
|
}
|
|
109
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/browser-agent.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/browser-agent.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 BrowserAgentAdapter,\n BrowserPageManager,\n WebAgentCore,\n resolveBrowserAgentRuntimeOptions,\n} from '@/common/browser-agent';\nimport { applyForceChromeSelectRendering } from '@/common/browser-agent-utils';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport { getDebug } from '@midscene/shared/logger';\nimport type {\n BrowserContext as PlaywrightBrowserContext,\n Page as PlaywrightPage,\n} from 'playwright';\nimport { WebPage as PlaywrightWebPage } from './page';\n\nconst debug = getDebug('playwright:browser-agent');\n\nconst createPlaywrightBrowserAdapter = (\n context: PlaywrightBrowserContext,\n): BrowserAgentAdapter<PlaywrightPage, PlaywrightPage> => ({\n pages: () => context.pages(),\n newPage: () => context.newPage(),\n isPageClosed: (page) => page.isClosed(),\n bringToFront: (page) => page.bringToFront(),\n onNewPage: (handler) => context.on('page', handler),\n offNewPage: (handler) => context.off('page', handler),\n resolveNewPage: (page) => page,\n});\n\nexport type PlaywrightBrowserAgentOpt = Omit<\n WebPageAgentOpt,\n 'forceSameTabNavigation'\n> & {\n autoFollowNewPage?: boolean;\n newPageTimeout?: number;\n};\n\nexport type PlaywrightBrowserAgentCreateOpt = PlaywrightBrowserAgentOpt & {\n initialPage?: PlaywrightPage;\n};\n\nexport class PlaywrightBrowserAgent extends WebAgentCore<PlaywrightWebPage> {\n private readonly pageManager: BrowserPageManager<\n PlaywrightPage,\n PlaywrightPage\n >;\n\n constructor(\n context: PlaywrightBrowserContext,\n initialPage: PlaywrightPage,\n opts?: PlaywrightBrowserAgentOpt,\n ) {\n if (!context) {\n throw new Error(\n '[midscene] PlaywrightBrowserAgent requires a valid Playwright browser context.',\n );\n }\n if (!initialPage) {\n throw new Error(\n '[midscene] PlaywrightBrowserAgent requires a valid initial page instance.',\n );\n }\n\n const { autoFollowNewPage, newPageTimeout, ...agentOpts } = opts ?? {};\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightBrowserAgent',\n pageScope: 'browser',\n forceSameTabNavigation: (opts as WebPageAgentOpt | undefined)\n ?.forceSameTabNavigation,\n autoFollowNewPage,\n newPageTimeout,\n });\n const { forceChromeSelectRendering } = agentOpts;\n const webPage = new PlaywrightWebPage(initialPage, {\n ...agentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n const pageManager = new BrowserPageManager({\n agentName: 'PlaywrightBrowserAgent',\n adapter: createPlaywrightBrowserAdapter(context),\n getActivePage: () => webPage.underlyingPage as PlaywrightPage,\n setActivePageValue: (page) => {\n webPage.underlyingPage = page;\n },\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n newPageTimeout: runtimeOptions.newPageTimeout,\n debug,\n });\n super(webPage, agentOpts);\n this.pageManager = pageManager;\n\n applyForceChromeSelectRendering(\n initialPage,\n 'playwright',\n forceChromeSelectRendering,\n );\n }\n\n static async create(\n context: PlaywrightBrowserContext,\n opts?: PlaywrightBrowserAgentCreateOpt,\n ) {\n const { initialPage, ...agentOpts } = opts ?? {};\n const page = initialPage ?? context.pages()[0] ?? (await context.newPage());\n\n return new PlaywrightBrowserAgent(context, page, agentOpts);\n }\n\n get activePage() {\n return this.pageManager.activePage;\n }\n\n pages() {\n return this.pageManager.pages();\n }\n\n async newPage() {\n return this.pageManager.newPage();\n }\n\n async setActivePage(page: PlaywrightPage) {\n await this.pageManager.setActivePage(page);\n }\n\n async waitForNewPage(\n action?: () => Promise<unknown> | unknown,\n opts?: { timeout?: number },\n ) {\n return this.pageManager.waitForNewPage(action, opts);\n }\n\n async destroy() {\n this.pageManager.destroy();\n await super.destroy();\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debug","getDebug","createPlaywrightBrowserAdapter","context","page","handler","PlaywrightBrowserAgent","WebAgentCore","opts","initialPage","agentOpts","action","Error","autoFollowNewPage","newPageTimeout","runtimeOptions","resolveBrowserAgentRuntimeOptions","forceChromeSelectRendering","webPage","PlaywrightWebPage","pageManager","BrowserPageManager","applyForceChromeSelectRendering"],"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
|
|
1
|
+
{"version":3,"file":"playwright/browser-agent.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/browser-agent.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 BrowserAgentAdapter,\n BrowserPageManager,\n WebAgentCore,\n resolveBrowserAgentRuntimeOptions,\n} from '@/common/browser-agent';\nimport { applyForceChromeSelectRendering } from '@/common/browser-agent-utils';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { AgentTestRunnerNodeDefinition } from '@midscene/core/agent';\nimport { getDebug } from '@midscene/shared/logger';\nimport type {\n BrowserContext as PlaywrightBrowserContext,\n Page as PlaywrightPage,\n} from 'playwright';\nimport { WebPage as PlaywrightWebPage } from './page';\nimport { playwrightAgentTestRunnerNodeDefinitions } from './test-runner/agent-nodes';\nimport type { PlaywrightTestRunnerOptions } from './test-runner/types';\n\nconst debug = getDebug('playwright:browser-agent');\n\nconst createPlaywrightBrowserAdapter = (\n context: PlaywrightBrowserContext,\n): BrowserAgentAdapter<PlaywrightPage, PlaywrightPage> => ({\n pages: () => context.pages(),\n newPage: () => context.newPage(),\n isPageClosed: (page) => page.isClosed(),\n bringToFront: (page) => page.bringToFront(),\n onNewPage: (handler) => context.on('page', handler),\n offNewPage: (handler) => context.off('page', handler),\n resolveNewPage: (page) => page,\n});\n\nexport type PlaywrightBrowserAgentOpt = Omit<\n WebPageAgentOpt,\n 'forceSameTabNavigation'\n> & {\n testRunner?: PlaywrightTestRunnerOptions;\n autoFollowNewPage?: boolean;\n newPageTimeout?: number;\n};\n\nexport type PlaywrightBrowserAgentCreateOpt = PlaywrightBrowserAgentOpt & {\n initialPage?: PlaywrightPage;\n};\n\nexport class PlaywrightBrowserAgent extends WebAgentCore<PlaywrightWebPage> {\n static override getTestRunnerNodeDefinitions(): readonly AgentTestRunnerNodeDefinition[] {\n return [\n ...WebAgentCore.getTestRunnerNodeDefinitions(),\n ...playwrightAgentTestRunnerNodeDefinitions,\n ];\n }\n\n readonly testRunner?: PlaywrightTestRunnerOptions;\n\n private readonly pageManager: BrowserPageManager<\n PlaywrightPage,\n PlaywrightPage\n >;\n\n constructor(\n context: PlaywrightBrowserContext,\n initialPage: PlaywrightPage,\n opts?: PlaywrightBrowserAgentOpt,\n ) {\n if (!context) {\n throw new Error(\n '[midscene] PlaywrightBrowserAgent requires a valid Playwright browser context.',\n );\n }\n if (!initialPage) {\n throw new Error(\n '[midscene] PlaywrightBrowserAgent requires a valid initial page instance.',\n );\n }\n\n const { autoFollowNewPage, newPageTimeout, ...agentOpts } = opts ?? {};\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightBrowserAgent',\n pageScope: 'browser',\n forceSameTabNavigation: (opts as WebPageAgentOpt | undefined)\n ?.forceSameTabNavigation,\n autoFollowNewPage,\n newPageTimeout,\n });\n const { forceChromeSelectRendering } = agentOpts;\n const webPage = new PlaywrightWebPage(initialPage, {\n ...agentOpts,\n forceSameTabNavigation: runtimeOptions.forceSameTabNavigation,\n });\n const pageManager = new BrowserPageManager({\n agentName: 'PlaywrightBrowserAgent',\n adapter: createPlaywrightBrowserAdapter(context),\n getActivePage: () => webPage.underlyingPage as PlaywrightPage,\n setActivePageValue: (page) => {\n webPage.underlyingPage = page;\n },\n autoFollowNewPage: runtimeOptions.autoFollowNewPage,\n newPageTimeout: runtimeOptions.newPageTimeout,\n debug,\n });\n super(webPage, agentOpts);\n this.pageManager = pageManager;\n this.testRunner = opts?.testRunner;\n\n applyForceChromeSelectRendering(\n initialPage,\n 'playwright',\n forceChromeSelectRendering,\n );\n }\n\n static async create(\n context: PlaywrightBrowserContext,\n opts?: PlaywrightBrowserAgentCreateOpt,\n ) {\n const { initialPage, ...agentOpts } = opts ?? {};\n const page = initialPage ?? context.pages()[0] ?? (await context.newPage());\n\n return new PlaywrightBrowserAgent(context, page, agentOpts);\n }\n\n get activePage() {\n return this.pageManager.activePage;\n }\n\n pages() {\n return this.pageManager.pages();\n }\n\n async newPage() {\n return this.pageManager.newPage();\n }\n\n async setActivePage(page: PlaywrightPage) {\n await this.pageManager.setActivePage(page);\n }\n\n async waitForNewPage(\n action?: () => Promise<unknown> | unknown,\n opts?: { timeout?: number },\n ) {\n return this.pageManager.waitForNewPage(action, opts);\n }\n\n async destroy() {\n this.pageManager.destroy();\n await super.destroy();\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debug","getDebug","createPlaywrightBrowserAdapter","context","page","handler","PlaywrightBrowserAgent","WebAgentCore","playwrightAgentTestRunnerNodeDefinitions","opts","initialPage","agentOpts","action","Error","autoFollowNewPage","newPageTimeout","runtimeOptions","resolveBrowserAgentRuntimeOptions","forceChromeSelectRendering","webPage","PlaywrightWebPage","pageManager","BrowserPageManager","applyForceChromeSelectRendering"],"mappings":"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"}
|
|
@@ -32,12 +32,30 @@ const browser_agent_utils_js_namespaceObject = require("../common/browser-agent-
|
|
|
32
32
|
const logger_namespaceObject = require("@midscene/shared/logger");
|
|
33
33
|
const base_page_js_namespaceObject = require("../puppeteer/base-page.js");
|
|
34
34
|
const external_page_js_namespaceObject = require("./page.js");
|
|
35
|
+
const agent_nodes_js_namespaceObject = require("./test-runner/agent-nodes.js");
|
|
36
|
+
function _define_property(obj, key, value) {
|
|
37
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
38
|
+
value: value,
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true
|
|
42
|
+
});
|
|
43
|
+
else obj[key] = value;
|
|
44
|
+
return obj;
|
|
45
|
+
}
|
|
35
46
|
const debug = (0, logger_namespaceObject.getDebug)('playwright:agent');
|
|
36
47
|
class PlaywrightPageAgent extends browser_agent_js_namespaceObject.WebAgentCore {
|
|
48
|
+
static getTestRunnerNodeDefinitions() {
|
|
49
|
+
return [
|
|
50
|
+
...browser_agent_js_namespaceObject.WebAgentCore.getTestRunnerNodeDefinitions(),
|
|
51
|
+
...agent_nodes_js_namespaceObject.playwrightAgentTestRunnerNodeDefinitions
|
|
52
|
+
];
|
|
53
|
+
}
|
|
37
54
|
constructor(page, opts){
|
|
38
55
|
if (!page) throw new Error('[midscene] PlaywrightPageAgent requires a valid Playwright page instance. Please make sure to pass a valid page object.');
|
|
39
56
|
const webPage = new external_page_js_namespaceObject.WebPage(page, opts);
|
|
40
|
-
super(webPage, opts);
|
|
57
|
+
super(webPage, opts), _define_property(this, "testRunner", void 0);
|
|
58
|
+
this.testRunner = opts?.testRunner;
|
|
41
59
|
const { forceSameTabNavigation, forceChromeSelectRendering } = opts ?? {};
|
|
42
60
|
const runtimeOptions = (0, browser_agent_js_namespaceObject.resolveBrowserAgentRuntimeOptions)({
|
|
43
61
|
agentName: 'PlaywrightPageAgent',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/page-agent.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/page-agent.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 WebAgentCore,\n resolveBrowserAgentRuntimeOptions,\n} from '@/common/browser-agent';\nimport { applyForceChromeSelectRendering } from '@/common/browser-agent-utils';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport { forceClosePopup } from '../puppeteer/base-page';\nimport { WebPage as PlaywrightWebPage } from './page';\n\nconst debug = getDebug('playwright:agent');\n\nexport class PlaywrightPageAgent extends WebAgentCore<PlaywrightWebPage> {\n constructor(page: PlaywrightPage, opts?:
|
|
1
|
+
{"version":3,"file":"playwright/page-agent.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/playwright/page-agent.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 WebAgentCore,\n resolveBrowserAgentRuntimeOptions,\n} from '@/common/browser-agent';\nimport { applyForceChromeSelectRendering } from '@/common/browser-agent-utils';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { AgentTestRunnerNodeDefinition } from '@midscene/core/agent';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport { forceClosePopup } from '../puppeteer/base-page';\nimport { WebPage as PlaywrightWebPage } from './page';\nimport { playwrightAgentTestRunnerNodeDefinitions } from './test-runner/agent-nodes';\nimport type { PlaywrightTestRunnerOptions } from './test-runner/types';\n\nconst debug = getDebug('playwright:agent');\n\nexport type PlaywrightPageAgentOpt = WebPageAgentOpt & {\n testRunner?: PlaywrightTestRunnerOptions;\n};\n\nexport class PlaywrightPageAgent extends WebAgentCore<PlaywrightWebPage> {\n static override getTestRunnerNodeDefinitions(): readonly AgentTestRunnerNodeDefinition[] {\n return [\n ...WebAgentCore.getTestRunnerNodeDefinitions(),\n ...playwrightAgentTestRunnerNodeDefinitions,\n ];\n }\n\n readonly testRunner?: PlaywrightTestRunnerOptions;\n\n constructor(page: PlaywrightPage, opts?: PlaywrightPageAgentOpt) {\n if (!page) {\n throw new Error(\n '[midscene] PlaywrightPageAgent requires a valid Playwright page instance. Please make sure to pass a valid page object.',\n );\n }\n const webPage = new PlaywrightWebPage(page, opts);\n super(webPage, opts);\n this.testRunner = opts?.testRunner;\n\n const { forceSameTabNavigation, forceChromeSelectRendering } = opts ?? {};\n const runtimeOptions = resolveBrowserAgentRuntimeOptions({\n agentName: 'PlaywrightPageAgent',\n pageScope: 'page',\n forceSameTabNavigation,\n });\n\n if (runtimeOptions.forceSameTabNavigation) {\n forceClosePopup(page, debug);\n }\n\n applyForceChromeSelectRendering(\n page,\n 'playwright',\n forceChromeSelectRendering,\n );\n }\n}\n\nexport { PlaywrightPageAgent as PlaywrightAgent };\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debug","getDebug","PlaywrightPageAgent","WebAgentCore","playwrightAgentTestRunnerNodeDefinitions","page","opts","Error","webPage","PlaywrightWebPage","forceSameTabNavigation","forceChromeSelectRendering","runtimeOptions","resolveBrowserAgentRuntimeOptions","forceClosePopup","applyForceChromeSelectRendering"],"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;;;;;;;;;;;;;;;;;;;;;;;;ACQA,MAAMI,QAAQC,AAAAA,IAAAA,uBAAAA,QAAAA,AAAAA,EAAS;AAMhB,MAAMC,4BAA4BC,iCAAAA,YAAYA;IACnD,OAAgB,+BAAyE;QACvF,OAAO;eACFA,iCAAAA,YAAAA,CAAAA,4BAAyC;eACzCC,+BAAAA,wCAAwCA;SAC5C;IACH;IAIA,YAAYC,IAAoB,EAAEC,IAA6B,CAAE;QAC/D,IAAI,CAACD,MACH,MAAM,IAAIE,MACR;QAGJ,MAAMC,UAAU,IAAIC,iCAAAA,OAAiBA,CAACJ,MAAMC;QAC5C,KAAK,CAACE,SAASF,OATjB,uBAAS,cAAT;QAUE,IAAI,CAAC,UAAU,GAAGA,MAAM;QAExB,MAAM,EAAEI,sBAAsB,EAAEC,0BAA0B,EAAE,GAAGL,QAAQ,CAAC;QACxE,MAAMM,iBAAiBC,AAAAA,IAAAA,iCAAAA,iCAAAA,AAAAA,EAAkC;YACvD,WAAW;YACX,WAAW;YACXH;QACF;QAEA,IAAIE,eAAe,sBAAsB,EACvCE,AAAAA,IAAAA,6BAAAA,eAAAA,AAAAA,EAAgBT,MAAML;QAGxBe,IAAAA,uCAAAA,+BAAAA,AAAAA,EACEV,MACA,cACAM;IAEJ;AACF"}
|