@tontoko/fast-playwright-mcp 0.0.4

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.
Files changed (107) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1047 -0
  3. package/cli.js +18 -0
  4. package/config.d.ts +124 -0
  5. package/index.d.ts +25 -0
  6. package/index.js +18 -0
  7. package/lib/actions.d.js +0 -0
  8. package/lib/batch/batch-executor.js +137 -0
  9. package/lib/browser-context-factory.js +252 -0
  10. package/lib/browser-server-backend.js +139 -0
  11. package/lib/config/constants.js +80 -0
  12. package/lib/config.js +405 -0
  13. package/lib/context.js +274 -0
  14. package/lib/diagnostics/common/diagnostic-base.js +63 -0
  15. package/lib/diagnostics/common/error-enrichment-utils.js +212 -0
  16. package/lib/diagnostics/common/index.js +56 -0
  17. package/lib/diagnostics/common/initialization-manager.js +210 -0
  18. package/lib/diagnostics/common/performance-tracker.js +132 -0
  19. package/lib/diagnostics/diagnostic-error.js +140 -0
  20. package/lib/diagnostics/diagnostic-level.js +123 -0
  21. package/lib/diagnostics/diagnostic-thresholds.js +347 -0
  22. package/lib/diagnostics/element-discovery.js +441 -0
  23. package/lib/diagnostics/enhanced-error-handler.js +376 -0
  24. package/lib/diagnostics/error-enrichment.js +157 -0
  25. package/lib/diagnostics/frame-reference-manager.js +179 -0
  26. package/lib/diagnostics/page-analyzer.js +639 -0
  27. package/lib/diagnostics/parallel-page-analyzer.js +129 -0
  28. package/lib/diagnostics/resource-manager.js +134 -0
  29. package/lib/diagnostics/smart-config.js +482 -0
  30. package/lib/diagnostics/smart-handle.js +118 -0
  31. package/lib/diagnostics/unified-system.js +717 -0
  32. package/lib/extension/cdp-relay.js +486 -0
  33. package/lib/extension/extension-context-factory.js +74 -0
  34. package/lib/extension/main.js +41 -0
  35. package/lib/file-utils.js +42 -0
  36. package/lib/generate-keys.js +75 -0
  37. package/lib/http-server.js +50 -0
  38. package/lib/in-process-client.js +64 -0
  39. package/lib/index.js +48 -0
  40. package/lib/javascript.js +90 -0
  41. package/lib/log.js +33 -0
  42. package/lib/loop/loop-claude.js +247 -0
  43. package/lib/loop/loop-open-ai.js +222 -0
  44. package/lib/loop/loop.js +174 -0
  45. package/lib/loop/main.js +46 -0
  46. package/lib/loopTools/context.js +76 -0
  47. package/lib/loopTools/main.js +65 -0
  48. package/lib/loopTools/perform.js +40 -0
  49. package/lib/loopTools/snapshot.js +37 -0
  50. package/lib/loopTools/tool.js +26 -0
  51. package/lib/manual-promise.js +125 -0
  52. package/lib/mcp/in-process-transport.js +91 -0
  53. package/lib/mcp/proxy-backend.js +127 -0
  54. package/lib/mcp/server.js +123 -0
  55. package/lib/mcp/transport.js +159 -0
  56. package/lib/package.js +28 -0
  57. package/lib/program.js +82 -0
  58. package/lib/response.js +493 -0
  59. package/lib/schemas/expectation.js +152 -0
  60. package/lib/session-log.js +210 -0
  61. package/lib/tab.js +417 -0
  62. package/lib/tools/base-tool-handler.js +141 -0
  63. package/lib/tools/batch-execute.js +150 -0
  64. package/lib/tools/common.js +65 -0
  65. package/lib/tools/console.js +60 -0
  66. package/lib/tools/diagnose/diagnose-analysis-runner.js +101 -0
  67. package/lib/tools/diagnose/diagnose-config-handler.js +130 -0
  68. package/lib/tools/diagnose/diagnose-report-builder.js +394 -0
  69. package/lib/tools/diagnose.js +147 -0
  70. package/lib/tools/dialogs.js +57 -0
  71. package/lib/tools/evaluate.js +67 -0
  72. package/lib/tools/files.js +53 -0
  73. package/lib/tools/find-elements.js +307 -0
  74. package/lib/tools/install.js +60 -0
  75. package/lib/tools/keyboard.js +93 -0
  76. package/lib/tools/mouse.js +110 -0
  77. package/lib/tools/navigate.js +82 -0
  78. package/lib/tools/network.js +50 -0
  79. package/lib/tools/pdf.js +46 -0
  80. package/lib/tools/screenshot.js +113 -0
  81. package/lib/tools/snapshot.js +158 -0
  82. package/lib/tools/tabs.js +97 -0
  83. package/lib/tools/tool.js +47 -0
  84. package/lib/tools/utils.js +131 -0
  85. package/lib/tools/wait.js +64 -0
  86. package/lib/tools.js +65 -0
  87. package/lib/types/batch.js +47 -0
  88. package/lib/types/diff.js +0 -0
  89. package/lib/types/performance.js +0 -0
  90. package/lib/types/threshold-base.js +0 -0
  91. package/lib/utils/array-utils.js +44 -0
  92. package/lib/utils/code-deduplication-utils.js +141 -0
  93. package/lib/utils/common-formatters.js +252 -0
  94. package/lib/utils/console-filter.js +64 -0
  95. package/lib/utils/diagnostic-report-utils.js +178 -0
  96. package/lib/utils/diff-formatter.js +126 -0
  97. package/lib/utils/disposable-manager.js +135 -0
  98. package/lib/utils/error-handler-middleware.js +77 -0
  99. package/lib/utils/image-processor.js +137 -0
  100. package/lib/utils/index.js +92 -0
  101. package/lib/utils/report-builder.js +189 -0
  102. package/lib/utils/request-logger.js +82 -0
  103. package/lib/utils/response-diff-detector.js +150 -0
  104. package/lib/utils/section-builder.js +62 -0
  105. package/lib/utils/tool-patterns.js +153 -0
  106. package/lib/utils.js +46 -0
  107. package/package.json +77 -0
@@ -0,0 +1,139 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/browser-server-backend.ts
21
+ import { fileURLToPath } from "node:url";
22
+ import debug from "debug";
23
+ import { z } from "zod";
24
+ import { Context } from "./context.js";
25
+ import { logUnhandledError } from "./log.js";
26
+ import { packageJSON } from "./package.js";
27
+ import { Response } from "./response.js";
28
+ import { SessionLog } from "./session-log.js";
29
+ import { defineTool } from "./tools/tool.js";
30
+ import { filteredTools } from "./tools.js";
31
+ var backendDebug = debug("pw:mcp:backend");
32
+
33
+ class BrowserServerBackend {
34
+ name = "Playwright";
35
+ version = packageJSON.version;
36
+ _tools;
37
+ _context;
38
+ _sessionLog;
39
+ _config;
40
+ _browserContextFactory;
41
+ constructor(config, factories) {
42
+ this._config = config;
43
+ this._browserContextFactory = factories[0];
44
+ this._tools = filteredTools(config);
45
+ if (factories.length > 1) {
46
+ this._tools.push(this._defineContextSwitchTool(factories));
47
+ }
48
+ }
49
+ async initialize(server) {
50
+ const capabilities = server.getClientCapabilities();
51
+ let rootPath;
52
+ if (capabilities.roots && (server.getClientVersion()?.name === "Visual Studio Code" || server.getClientVersion()?.name === "Visual Studio Code - Insiders")) {
53
+ const { roots } = await server.listRoots();
54
+ const firstRootUri = roots[0]?.uri;
55
+ const url = firstRootUri ? new URL(firstRootUri) : undefined;
56
+ rootPath = url ? fileURLToPath(url) : undefined;
57
+ }
58
+ this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, rootPath) : undefined;
59
+ this._context = new Context({
60
+ tools: this._tools,
61
+ config: this._config,
62
+ browserContextFactory: this._browserContextFactory,
63
+ sessionLog: this._sessionLog,
64
+ clientInfo: { ...server.getClientVersion(), rootPath }
65
+ });
66
+ }
67
+ tools() {
68
+ return this._tools.map((tool) => tool.schema);
69
+ }
70
+ async callTool(schema, rawArguments) {
71
+ if (!this._context) {
72
+ throw new Error("Context not initialized. Call initialize() first.");
73
+ }
74
+ const context = this._context;
75
+ const parsedArguments = schema.inputSchema.parse(rawArguments || {});
76
+ const response = new Response(context, schema.name, parsedArguments, parsedArguments.expectation);
77
+ const matchedTool = this._tools.find((t) => t.schema.name === schema.name);
78
+ if (!matchedTool) {
79
+ throw new Error(`Tool not found: ${schema.name}`);
80
+ }
81
+ context.setRunningTool(true);
82
+ try {
83
+ await matchedTool.handle(context, parsedArguments, response);
84
+ await response.finish();
85
+ this._sessionLog?.logResponse(response);
86
+ } catch (error) {
87
+ backendDebug(`Error executing tool ${schema.name}:`, error);
88
+ response.addError(String(error));
89
+ } finally {
90
+ context.setRunningTool(false);
91
+ }
92
+ return response.serialize();
93
+ }
94
+ serverClosed() {
95
+ this._context?.dispose().catch(logUnhandledError);
96
+ }
97
+ _defineContextSwitchTool(factories) {
98
+ const self = this;
99
+ return defineTool({
100
+ capability: "core",
101
+ schema: {
102
+ name: "browser_connect",
103
+ title: "Connect to a browser context",
104
+ description: [
105
+ "Connect to a browser using one of the available methods:",
106
+ ...factories.map((factory) => `- "${factory.name}": ${factory.description}`)
107
+ ].join(`
108
+ `),
109
+ inputSchema: z.object({
110
+ method: z.enum(factories.map((f) => f.name)).default(factories[0].name).describe("The method to use to connect to the browser")
111
+ }),
112
+ type: "readOnly"
113
+ },
114
+ async handle(_context, params, response) {
115
+ const selectedFactory = factories.find((f) => f.name === params.method);
116
+ if (!selectedFactory) {
117
+ response.addError(`Unknown connection method: ${params.method}`);
118
+ return;
119
+ }
120
+ await self._setContextFactory(selectedFactory);
121
+ response.addResult("Successfully changed connection method.");
122
+ }
123
+ });
124
+ }
125
+ async _setContextFactory(newFactory) {
126
+ if (this._context) {
127
+ const options = {
128
+ ...this._context.options,
129
+ browserContextFactory: newFactory
130
+ };
131
+ await this._context.dispose();
132
+ this._context = new Context(options);
133
+ }
134
+ this._browserContextFactory = newFactory;
135
+ }
136
+ }
137
+ export {
138
+ BrowserServerBackend
139
+ };
@@ -0,0 +1,80 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/config/constants.ts
21
+ var TIMEOUTS = {
22
+ DEFAULT_PAGE_TIMEOUT: 5000,
23
+ LOAD_STATE_TIMEOUT: 5000,
24
+ NETWORK_IDLE_TIMEOUT: 2000,
25
+ STABILITY_TIMEOUT: 3000,
26
+ RETRY_DELAY: 500,
27
+ ELEMENT_DISCOVERY_TIMEOUT: 500,
28
+ PAGE_ANALYSIS_TIMEOUT: 1000,
29
+ PARALLEL_ANALYSIS_TIMEOUT: 2000,
30
+ WAIT_FOR_COMPLETION: 1000,
31
+ STABILITY_WAIT: 1500,
32
+ PING_TIMEOUT: 5000,
33
+ HEARTBEAT_INTERVAL: 3000,
34
+ SESSION_LOG_FLUSH: 1000,
35
+ FRAME_ACCESS_TIMEOUT: 1000,
36
+ INITIALIZATION_TIMEOUT: 5000,
37
+ MAX_WAIT_TIME: 30000,
38
+ SHORT_DELAY: 500,
39
+ MEDIUM_DELAY: 1000,
40
+ LONG_DELAY: 3000
41
+ };
42
+ var THRESHOLDS = {
43
+ HIGH_PERFORMANCE_IMPACT: 1000,
44
+ EXECUTION_TIME_WARNING: 2000,
45
+ EXECUTION_TIME_SLOW: 5000,
46
+ LARGE_SUBTREE_ELEMENTS: 1000,
47
+ SMALL_SUBTREE_ELEMENTS: 500,
48
+ ELEMENTS_WARNING: 1500,
49
+ ELEMENTS_DANGER: 3000,
50
+ VISIBLE_ELEMENTS_LIMIT: 1000,
51
+ DESCENDANT_COUNT_WARNING: 500,
52
+ HIGH_Z_INDEX: 1000,
53
+ EXCESSIVE_Z_INDEX: 1000,
54
+ LARGE_IMAGE_SIZE: 1000,
55
+ MEDIUM_IMAGE_SIZE: 500,
56
+ PERFORMANCE_TRACKER_HISTORY_SIZE: 1000,
57
+ COMPLEXITY_HIGH: 2000,
58
+ COMPLEXITY_MEDIUM: 1000
59
+ };
60
+ var DURATIONS = {
61
+ FIVE_MINUTES: 5 * 60 * 1000,
62
+ TEN_MINUTES: 10 * 60 * 1000,
63
+ ONE_HOUR: 60 * 60 * 1000,
64
+ ONE_DAY: 24 * 60 * 60 * 1000,
65
+ ONE_YEAR: 365 * 24 * 60 * 60 * 1000
66
+ };
67
+ var STRING_LIMITS = {
68
+ CDP_MESSAGE_PREVIEW: 500,
69
+ DEFAULT_TRUNCATION: 1000
70
+ };
71
+ var NETWORK = {
72
+ WS_NORMAL_CLOSURE: 1000
73
+ };
74
+ export {
75
+ TIMEOUTS,
76
+ THRESHOLDS,
77
+ STRING_LIMITS,
78
+ NETWORK,
79
+ DURATIONS
80
+ };
package/lib/config.js ADDED
@@ -0,0 +1,405 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/config.ts
21
+ import { promises as fsPromises } from "node:fs";
22
+ import { platform, tmpdir } from "node:os";
23
+ import { join as pathJoin } from "node:path";
24
+ import { devices } from "playwright";
25
+ import { sanitizeForFilePath } from "./utils.js";
26
+ var defaultConfig = {
27
+ browser: {
28
+ browserName: "chromium",
29
+ launchOptions: {
30
+ channel: "chrome",
31
+ headless: platform() === "linux" && !process.env.DISPLAY,
32
+ chromiumSandbox: true
33
+ },
34
+ contextOptions: {
35
+ viewport: null
36
+ }
37
+ },
38
+ network: {
39
+ allowedOrigins: undefined,
40
+ blockedOrigins: undefined
41
+ },
42
+ server: {},
43
+ saveTrace: false
44
+ };
45
+ function resolveConfig(config) {
46
+ return mergeConfig(defaultConfig, config);
47
+ }
48
+ async function resolveCLIConfig(cliOptions) {
49
+ const configInFile = await loadConfig(cliOptions.config);
50
+ const envOverrides = configFromEnv();
51
+ const cliOverrides = configFromCLIOptions(cliOptions);
52
+ let result = defaultConfig;
53
+ result = mergeConfig(result, configInFile);
54
+ result = mergeConfig(result, envOverrides);
55
+ result = mergeConfig(result, cliOverrides);
56
+ return result;
57
+ }
58
+ function parseBrowserType(browser) {
59
+ if (isChromiumVariant(browser)) {
60
+ return { browserName: "chromium", channel: browser };
61
+ }
62
+ if (browser === "firefox") {
63
+ return { browserName: "firefox", channel: undefined };
64
+ }
65
+ if (browser === "webkit") {
66
+ return { browserName: "webkit", channel: undefined };
67
+ }
68
+ return { browserName: undefined, channel: undefined };
69
+ }
70
+ function isChromiumVariant(browser) {
71
+ const chromiumVariants = [
72
+ "chrome",
73
+ "chrome-beta",
74
+ "chrome-canary",
75
+ "chrome-dev",
76
+ "chromium",
77
+ "msedge",
78
+ "msedge-beta",
79
+ "msedge-canary",
80
+ "msedge-dev"
81
+ ];
82
+ return chromiumVariants.includes(browser);
83
+ }
84
+ function createLaunchOptions(cliOptions, channel) {
85
+ const launchOptions = {
86
+ channel,
87
+ executablePath: cliOptions.executablePath,
88
+ headless: cliOptions.headless
89
+ };
90
+ applySandboxSettings(launchOptions, cliOptions);
91
+ applyProxySettings(launchOptions, cliOptions);
92
+ return launchOptions;
93
+ }
94
+ function applySandboxSettings(launchOptions, cliOptions) {
95
+ if (cliOptions.sandbox === false) {
96
+ launchOptions.chromiumSandbox = false;
97
+ }
98
+ }
99
+ function applyProxySettings(launchOptions, cliOptions) {
100
+ if (!cliOptions.proxyServer) {
101
+ return;
102
+ }
103
+ launchOptions.proxy = {
104
+ server: cliOptions.proxyServer,
105
+ ...cliOptions.proxyBypass && { bypass: cliOptions.proxyBypass }
106
+ };
107
+ }
108
+ function createContextOptions(cliOptions) {
109
+ const contextOptions = cliOptions.device ? devices[cliOptions.device] || {} : {};
110
+ applyStorageOptions(contextOptions, cliOptions);
111
+ applyViewportOptions(contextOptions, cliOptions);
112
+ applySecurityOptions(contextOptions, cliOptions);
113
+ return contextOptions;
114
+ }
115
+ function applyStorageOptions(contextOptions, cliOptions) {
116
+ if (cliOptions.storageState) {
117
+ contextOptions.storageState = cliOptions.storageState;
118
+ }
119
+ }
120
+ function applyViewportOptions(contextOptions, cliOptions) {
121
+ if (cliOptions.userAgent) {
122
+ contextOptions.userAgent = cliOptions.userAgent;
123
+ }
124
+ if (cliOptions.viewportSize) {
125
+ contextOptions.viewport = parseViewportSize(cliOptions.viewportSize);
126
+ }
127
+ }
128
+ function applySecurityOptions(contextOptions, cliOptions) {
129
+ if (cliOptions.ignoreHttpsErrors) {
130
+ contextOptions.ignoreHTTPSErrors = true;
131
+ }
132
+ if (cliOptions.blockServiceWorkers) {
133
+ contextOptions.serviceWorkers = "block";
134
+ }
135
+ }
136
+ function parseViewportSize(viewportSize) {
137
+ try {
138
+ const [width, height] = viewportSize.split(",").map((n) => +n);
139
+ if (Number.isNaN(width) || Number.isNaN(height)) {
140
+ throw new Error("bad values");
141
+ }
142
+ return { width, height };
143
+ } catch {
144
+ throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"');
145
+ }
146
+ }
147
+ function validateDeviceAndCDPOptions(cliOptions) {
148
+ if (cliOptions.device && cliOptions.cdpEndpoint) {
149
+ throw new Error("Device emulation is not supported with cdpEndpoint.");
150
+ }
151
+ }
152
+ function configFromCLIOptions(cliOptions) {
153
+ const browserInfo = getBrowserInfo(cliOptions);
154
+ validateDeviceAndCDPOptions(cliOptions);
155
+ return buildFinalConfig(cliOptions, browserInfo);
156
+ }
157
+ function buildFinalConfig(cliOptions, browserInfo) {
158
+ return assembleConfigFromParts(cliOptions, browserInfo);
159
+ }
160
+ function assembleConfigFromParts(cliOptions, browserInfo) {
161
+ const configParts = createAllConfigParts(cliOptions, browserInfo);
162
+ return mergeAllConfigParts(configParts);
163
+ }
164
+ function createAllConfigParts(cliOptions, browserInfo) {
165
+ return {
166
+ browserConfig: createBrowserConfig(cliOptions, browserInfo.browserName, browserInfo.channel),
167
+ serverConfig: createServerConfig(cliOptions),
168
+ networkConfig: createNetworkConfig(cliOptions),
169
+ miscConfig: createMiscellaneousConfig(cliOptions)
170
+ };
171
+ }
172
+ function mergeAllConfigParts(configParts) {
173
+ return {
174
+ ...configParts.browserConfig,
175
+ ...configParts.serverConfig,
176
+ ...configParts.networkConfig,
177
+ ...configParts.miscConfig
178
+ };
179
+ }
180
+ function getBrowserInfo(cliOptions) {
181
+ return cliOptions.browser ? parseBrowserType(cliOptions.browser) : { browserName: undefined, channel: undefined };
182
+ }
183
+ function createMiscellaneousConfig(cliOptions) {
184
+ return {
185
+ capabilities: cliOptions.caps,
186
+ saveSession: cliOptions.saveSession,
187
+ saveTrace: cliOptions.saveTrace,
188
+ outputDir: cliOptions.outputDir,
189
+ imageResponses: cliOptions.imageResponses
190
+ };
191
+ }
192
+ function createBrowserConfig(cliOptions, browserName, channel) {
193
+ const browser = {
194
+ isolated: cliOptions.isolated,
195
+ userDataDir: cliOptions.userDataDir,
196
+ launchOptions: createLaunchOptions(cliOptions, channel),
197
+ contextOptions: createContextOptions(cliOptions),
198
+ cdpEndpoint: cliOptions.cdpEndpoint
199
+ };
200
+ if (browserName !== undefined) {
201
+ browser.browserName = browserName;
202
+ }
203
+ return { browser };
204
+ }
205
+ function createServerConfig(cliOptions) {
206
+ return {
207
+ server: {
208
+ port: cliOptions.port,
209
+ host: cliOptions.host
210
+ }
211
+ };
212
+ }
213
+ function createNetworkConfig(cliOptions) {
214
+ return {
215
+ network: {
216
+ allowedOrigins: cliOptions.allowedOrigins,
217
+ blockedOrigins: cliOptions.blockedOrigins
218
+ }
219
+ };
220
+ }
221
+ function configFromEnv() {
222
+ const options = buildEnvOptions();
223
+ return configFromCLIOptions(options);
224
+ }
225
+ function buildEnvOptions() {
226
+ const options = {};
227
+ populateAllOptions(options);
228
+ return options;
229
+ }
230
+ function populateAllOptions(options) {
231
+ populateNetworkOptions(options);
232
+ populateBrowserOptions(options);
233
+ populateDeviceOptions(options);
234
+ populateProxyOptions(options);
235
+ populateOutputOptions(options);
236
+ populateMiscellaneousOptions(options);
237
+ }
238
+ function populateNetworkOptions(options) {
239
+ options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
240
+ options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
241
+ options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
242
+ options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
243
+ options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
244
+ }
245
+ function populateBrowserOptions(options) {
246
+ options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
247
+ options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
248
+ options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
249
+ options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
250
+ options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
251
+ options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
252
+ }
253
+ function populateDeviceOptions(options) {
254
+ options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
255
+ options.viewportSize = envToString(process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
256
+ options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
257
+ options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);
258
+ options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
259
+ }
260
+ function populateProxyOptions(options) {
261
+ options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);
262
+ options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);
263
+ }
264
+ function populateOutputOptions(options) {
265
+ options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
266
+ options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);
267
+ if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === "omit") {
268
+ options.imageResponses = "omit";
269
+ }
270
+ }
271
+ function populateMiscellaneousOptions(options) {
272
+ options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
273
+ options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
274
+ options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
275
+ }
276
+ async function loadConfig(configFile) {
277
+ if (!configFile) {
278
+ return {};
279
+ }
280
+ try {
281
+ const configContent = await fsPromises.readFile(configFile, "utf8");
282
+ validateConfigContent(configContent);
283
+ const config = JSON.parse(configContent);
284
+ sanitizeConfigIfNeeded(config);
285
+ return config;
286
+ } catch (error) {
287
+ throw new Error(`Failed to load config file: ${configFile}, ${error}`);
288
+ }
289
+ }
290
+ function validateConfigContent(configContent) {
291
+ if (configContent.length > 1024 * 1024) {
292
+ throw new Error("Configuration file too large");
293
+ }
294
+ if (configContent.includes("__proto__") || configContent.includes("constructor")) {
295
+ throw new Error("Configuration file contains potentially dangerous content");
296
+ }
297
+ }
298
+ function sanitizeConfigIfNeeded(config) {
299
+ if (config && typeof config === "object") {
300
+ sanitizeConfigObject(config);
301
+ }
302
+ }
303
+ function sanitizeConfigObject(obj) {
304
+ if (!obj || typeof obj !== "object") {
305
+ return;
306
+ }
307
+ const dangerousProps = ["__proto__", "constructor", "prototype"];
308
+ for (const prop of dangerousProps) {
309
+ if (prop in obj) {
310
+ delete obj[prop];
311
+ }
312
+ }
313
+ for (const value of Object.values(obj)) {
314
+ if (typeof value === "object" && value !== null) {
315
+ sanitizeConfigObject(value);
316
+ }
317
+ }
318
+ }
319
+ async function outputFile(config, rootPath, name) {
320
+ const outputDir = config.outputDir ?? (rootPath ? pathJoin(rootPath, ".playwright-mcp") : undefined) ?? pathJoin(tmpdir(), "playwright-mcp-output", sanitizeForFilePath(new Date().toISOString()));
321
+ await fsPromises.mkdir(outputDir, { recursive: true });
322
+ const fileName = sanitizeForFilePath(name);
323
+ return pathJoin(outputDir, fileName);
324
+ }
325
+ function pickDefined(obj) {
326
+ return Object.fromEntries(Object.entries(obj ?? {}).filter(([_, v]) => v !== undefined));
327
+ }
328
+ function mergeConfig(base, overrides) {
329
+ const browser = createMergedBrowserConfig(base, overrides);
330
+ return {
331
+ ...pickDefined(base),
332
+ ...pickDefined(overrides),
333
+ browser,
334
+ network: {
335
+ ...pickDefined(base.network),
336
+ ...pickDefined(overrides.network)
337
+ },
338
+ server: {
339
+ ...pickDefined(base.server),
340
+ ...pickDefined(overrides.server)
341
+ }
342
+ };
343
+ }
344
+ function createMergedBrowserConfig(base, overrides) {
345
+ const browser = {
346
+ ...pickDefined(base.browser),
347
+ ...pickDefined(overrides.browser),
348
+ browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? "chromium",
349
+ isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,
350
+ launchOptions: {
351
+ ...pickDefined(base.browser?.launchOptions),
352
+ ...pickDefined(overrides.browser?.launchOptions),
353
+ ...{ assistantMode: true }
354
+ },
355
+ contextOptions: {
356
+ ...pickDefined(base.browser?.contextOptions),
357
+ ...pickDefined(overrides.browser?.contextOptions)
358
+ }
359
+ };
360
+ handleNonChromiumChannel(browser);
361
+ return browser;
362
+ }
363
+ function handleNonChromiumChannel(browser) {
364
+ if (browser.browserName !== "chromium" && browser.launchOptions) {
365
+ browser.launchOptions.channel = undefined;
366
+ }
367
+ }
368
+ function semicolonSeparatedList(value) {
369
+ if (!value) {
370
+ return;
371
+ }
372
+ return value.split(";").map((v) => v.trim());
373
+ }
374
+ function commaSeparatedList(value) {
375
+ if (!value) {
376
+ return;
377
+ }
378
+ return value.split(",").map((v) => v.trim());
379
+ }
380
+ function envToNumber(value) {
381
+ if (!value) {
382
+ return;
383
+ }
384
+ return +value;
385
+ }
386
+ function envToBoolean(value) {
387
+ if (value === "true" || value === "1") {
388
+ return true;
389
+ }
390
+ if (value === "false" || value === "0") {
391
+ return false;
392
+ }
393
+ }
394
+ function envToString(value) {
395
+ return value ? value.trim() : undefined;
396
+ }
397
+ export {
398
+ semicolonSeparatedList,
399
+ resolveConfig,
400
+ resolveCLIConfig,
401
+ outputFile,
402
+ isChromiumVariant,
403
+ configFromCLIOptions,
404
+ commaSeparatedList
405
+ };