@treegress.com/treegress-browser-mcp 0.0.56-treegress.3

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 (67) hide show
  1. package/README.md +68 -0
  2. package/cli.js +25 -0
  3. package/config.d.ts +223 -0
  4. package/index.d.ts +23 -0
  5. package/index.js +19 -0
  6. package/mcp/browser/browserContextFactory.js +332 -0
  7. package/mcp/browser/browserServerBackend.js +105 -0
  8. package/mcp/browser/config.js +489 -0
  9. package/mcp/browser/configIni.js +194 -0
  10. package/mcp/browser/context.js +302 -0
  11. package/mcp/browser/domSnapshot.js +307 -0
  12. package/mcp/browser/logFile.js +96 -0
  13. package/mcp/browser/response.js +299 -0
  14. package/mcp/browser/sessionLog.js +75 -0
  15. package/mcp/browser/tab.js +1193 -0
  16. package/mcp/browser/tools/common.js +63 -0
  17. package/mcp/browser/tools/config.js +41 -0
  18. package/mcp/browser/tools/console.js +65 -0
  19. package/mcp/browser/tools/cookies.js +152 -0
  20. package/mcp/browser/tools/devtools.js +42 -0
  21. package/mcp/browser/tools/dialogs.js +59 -0
  22. package/mcp/browser/tools/evaluate.js +61 -0
  23. package/mcp/browser/tools/files.js +58 -0
  24. package/mcp/browser/tools/form.js +63 -0
  25. package/mcp/browser/tools/install.js +73 -0
  26. package/mcp/browser/tools/keyboard.js +151 -0
  27. package/mcp/browser/tools/mouse.js +159 -0
  28. package/mcp/browser/tools/navigate.js +105 -0
  29. package/mcp/browser/tools/network.js +92 -0
  30. package/mcp/browser/tools/pdf.js +48 -0
  31. package/mcp/browser/tools/route.js +140 -0
  32. package/mcp/browser/tools/runCode.js +76 -0
  33. package/mcp/browser/tools/screenshot.js +86 -0
  34. package/mcp/browser/tools/snapshot.js +207 -0
  35. package/mcp/browser/tools/storage.js +67 -0
  36. package/mcp/browser/tools/tabs.js +67 -0
  37. package/mcp/browser/tools/tool.js +47 -0
  38. package/mcp/browser/tools/tracing.js +75 -0
  39. package/mcp/browser/tools/utils.js +88 -0
  40. package/mcp/browser/tools/verify.js +143 -0
  41. package/mcp/browser/tools/video.js +89 -0
  42. package/mcp/browser/tools/wait.js +63 -0
  43. package/mcp/browser/tools/webstorage.js +223 -0
  44. package/mcp/browser/tools.js +96 -0
  45. package/mcp/browser/watchdog.js +44 -0
  46. package/mcp/config.d.js +16 -0
  47. package/mcp/extension/cdpRelay.js +354 -0
  48. package/mcp/extension/extensionContextFactory.js +77 -0
  49. package/mcp/extension/protocol.js +28 -0
  50. package/mcp/index.js +61 -0
  51. package/mcp/log.js +35 -0
  52. package/mcp/program.js +126 -0
  53. package/mcp/sdk/exports.js +28 -0
  54. package/mcp/sdk/http.js +172 -0
  55. package/mcp/sdk/inProcessTransport.js +71 -0
  56. package/mcp/sdk/server.js +223 -0
  57. package/mcp/sdk/tool.js +54 -0
  58. package/mcp/test/browserBackend.js +98 -0
  59. package/mcp/test/generatorTools.js +122 -0
  60. package/mcp/test/plannerTools.js +145 -0
  61. package/mcp/test/seed.js +82 -0
  62. package/mcp/test/streams.js +44 -0
  63. package/mcp/test/testBackend.js +99 -0
  64. package/mcp/test/testContext.js +285 -0
  65. package/mcp/test/testTool.js +30 -0
  66. package/mcp/test/testTools.js +108 -0
  67. package/package.json +46 -0
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var browserServerBackend_exports = {};
20
+ __export(browserServerBackend_exports, {
21
+ BrowserServerBackend: () => BrowserServerBackend
22
+ });
23
+ module.exports = __toCommonJS(browserServerBackend_exports);
24
+ var import_path = require("path");
25
+ var import_context = require("./context");
26
+ var import_log = require("../log");
27
+ var import_response = require("./response");
28
+ var import_sessionLog = require("./sessionLog");
29
+ var import_tools = require("./tools");
30
+ var import_tool = require("../sdk/tool");
31
+ class BrowserServerBackend {
32
+ constructor(config, factory, options = {}) {
33
+ this._config = config;
34
+ this._browserContextFactory = factory;
35
+ this._tools = options.allTools ? import_tools.browserTools : (0, import_tools.filteredTools)(config);
36
+ }
37
+ async initialize(clientInfo) {
38
+ this._sessionLog = this._config.saveSession ? await import_sessionLog.SessionLog.create(this._config, clientInfo) : void 0;
39
+ this._context = new import_context.Context({
40
+ config: this._config,
41
+ browserContextFactory: this._browserContextFactory,
42
+ sessionLog: this._sessionLog,
43
+ clientInfo
44
+ });
45
+ }
46
+ async listTools() {
47
+ return this._tools.map((tool) => (0, import_tool.toMcpTool)(tool.schema));
48
+ }
49
+ async callTool(name, rawArguments) {
50
+ const tool = this._tools.find((tool2) => tool2.schema.name === name);
51
+ if (!tool) {
52
+ return {
53
+ content: [{ type: "text", text: `### Error
54
+ Tool "${name}" not found` }],
55
+ isError: true
56
+ };
57
+ }
58
+ const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
59
+ const rawCwd = rawArguments?._meta && typeof rawArguments._meta === "object" && rawArguments._meta.cwd;
60
+ const cwd = resolveClientWorkspace(rawCwd);
61
+ const context = this._context;
62
+ const response = new import_response.Response(context, name, parsedArguments, cwd);
63
+ context.setRunningTool(name);
64
+ let responseObject;
65
+ try {
66
+ await tool.handle(context, parsedArguments, response);
67
+ responseObject = await response.serialize();
68
+ this._sessionLog?.logResponse(name, parsedArguments, responseObject);
69
+ } catch (error) {
70
+ return {
71
+ content: [{ type: "text", text: `### Error
72
+ ${String(error)}` }],
73
+ isError: true
74
+ };
75
+ } finally {
76
+ context.setRunningTool(void 0);
77
+ }
78
+ return responseObject;
79
+ }
80
+ serverClosed() {
81
+ void this._context?.dispose().catch(import_log.logUnhandledError);
82
+ }
83
+ }
84
+
85
+ function resolveClientWorkspace(rawValue) {
86
+ if (typeof rawValue !== "string")
87
+ return void 0;
88
+ const value = rawValue.trim();
89
+ if (!value)
90
+ return void 0;
91
+ if (value.startsWith("file://")) {
92
+ try {
93
+ return import_path.fileURLToPath(value);
94
+ } catch (error) {
95
+ return void 0;
96
+ }
97
+ }
98
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value))
99
+ return void 0;
100
+ return value;
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ BrowserServerBackend
105
+ });
@@ -0,0 +1,489 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var config_exports = {};
30
+ __export(config_exports, {
31
+ commaSeparatedList: () => commaSeparatedList,
32
+ configFromCLIOptions: () => configFromCLIOptions,
33
+ configFromEnv: () => configFromEnv,
34
+ defaultConfig: () => defaultConfig,
35
+ dotenvFileLoader: () => dotenvFileLoader,
36
+ enumParser: () => enumParser,
37
+ headerParser: () => headerParser,
38
+ loadConfig: () => loadConfig,
39
+ mergeConfig: () => mergeConfig,
40
+ numberParser: () => numberParser,
41
+ outputDir: () => outputDir,
42
+ outputFile: () => outputFile,
43
+ resolutionParser: () => resolutionParser,
44
+ resolveCLIConfig: () => resolveCLIConfig,
45
+ resolveConfig: () => resolveConfig,
46
+ semicolonSeparatedList: () => semicolonSeparatedList,
47
+ validateConfig: () => validateConfig,
48
+ workspaceDir: () => workspaceDir,
49
+ workspaceFile: () => workspaceFile
50
+ });
51
+ module.exports = __toCommonJS(config_exports);
52
+ var import_fs = __toESM(require("fs"));
53
+ var import_os = __toESM(require("os"));
54
+ var import_path = __toESM(require("path"));
55
+ var import_server = require("playwright-core/lib/server");
56
+ var import_playwright_core = require("playwright-core");
57
+ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
58
+ var import_configIni = require("./configIni");
59
+ var import_server2 = require("../sdk/server");
60
+ async function fileExistsAsync(resolved) {
61
+ try {
62
+ return (await import_fs.default.promises.stat(resolved)).isFile();
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+ const defaultConfig = {
68
+ browser: {
69
+ browserName: "chromium",
70
+ launchOptions: {
71
+ channel: "chrome",
72
+ headless: import_os.default.platform() === "linux" && !process.env.DISPLAY
73
+ },
74
+ contextOptions: {
75
+ viewport: null
76
+ },
77
+ isolated: false
78
+ },
79
+ console: {
80
+ level: "info"
81
+ },
82
+ network: {
83
+ allowedOrigins: void 0,
84
+ blockedOrigins: void 0
85
+ },
86
+ server: {},
87
+ saveTrace: false,
88
+ snapshot: {
89
+ mode: "incremental",
90
+ engine: "dom",
91
+ domSerializerPath: void 0,
92
+ domFallbackToAria: false,
93
+ domNonstrict: false,
94
+ output: "stdout"
95
+ },
96
+ timeouts: {
97
+ action: 5e3,
98
+ navigation: 6e4
99
+ }
100
+ };
101
+ async function resolveConfig(config) {
102
+ return mergeConfig(defaultConfig, config);
103
+ }
104
+ async function resolveCLIConfig(cliOptions) {
105
+ const envOverrides = configFromEnv();
106
+ const cliOverrides = configFromCLIOptions(cliOptions);
107
+ const configFile = cliOverrides.configFile ?? envOverrides.configFile;
108
+ const configInFile = await loadConfig(configFile);
109
+ let result = defaultConfig;
110
+ result = mergeConfig(result, configInFile);
111
+ result = mergeConfig(result, envOverrides);
112
+ result = mergeConfig(result, cliOverrides);
113
+ result.configFile = configFile;
114
+ await validateConfig(result);
115
+ return result;
116
+ }
117
+ async function validateConfig(config) {
118
+ if (config.browser.browserName === "chromium" && config.browser.launchOptions.chromiumSandbox === void 0) {
119
+ if (process.platform === "linux")
120
+ config.browser.launchOptions.chromiumSandbox = config.browser.launchOptions.channel !== "chromium";
121
+ else
122
+ config.browser.launchOptions.chromiumSandbox = true;
123
+ }
124
+ if (config.saveVideo && !checkFfmpeg()) {
125
+ console.error(`
126
+ Error: ffmpeg required to save the video is not installed.`);
127
+ console.error(`
128
+ Please run the command below. It will install a local copy of ffmpeg and will not change any system-wide settings.`);
129
+ console.error(`
130
+ npx treegress-browser-core install ffmpeg
131
+ `);
132
+ process.exit(1);
133
+ }
134
+ if (config.browser.initScript) {
135
+ for (const script of config.browser.initScript) {
136
+ if (!await fileExistsAsync(script))
137
+ throw new Error(`Init script file does not exist: ${script}`);
138
+ }
139
+ }
140
+ if (config.browser.initPage) {
141
+ for (const page of config.browser.initPage) {
142
+ if (!await fileExistsAsync(page))
143
+ throw new Error(`Init page file does not exist: ${page}`);
144
+ }
145
+ }
146
+ if (config.sharedBrowserContext && config.saveVideo)
147
+ throw new Error("saveVideo is not supported when sharedBrowserContext is true");
148
+ if (config.snapshot?.engine !== "aria" && config.snapshot?.engine !== "dom")
149
+ throw new Error(`Invalid snapshot.engine: ${config.snapshot?.engine}. Valid values: aria, dom`);
150
+ }
151
+ function configFromCLIOptions(cliOptions) {
152
+ let browserName;
153
+ let channel;
154
+ switch (cliOptions.browser) {
155
+ case "chrome":
156
+ case "chrome-beta":
157
+ case "chrome-canary":
158
+ case "chrome-dev":
159
+ case "chromium":
160
+ case "msedge":
161
+ case "msedge-beta":
162
+ case "msedge-canary":
163
+ case "msedge-dev":
164
+ browserName = "chromium";
165
+ channel = cliOptions.browser;
166
+ break;
167
+ case "firefox":
168
+ browserName = "firefox";
169
+ break;
170
+ case "webkit":
171
+ browserName = "webkit";
172
+ break;
173
+ }
174
+ const launchOptions = {
175
+ channel,
176
+ executablePath: cliOptions.executablePath,
177
+ headless: cliOptions.headless
178
+ };
179
+ if (cliOptions.sandbox !== void 0)
180
+ launchOptions.chromiumSandbox = cliOptions.sandbox;
181
+ if (cliOptions.proxyServer) {
182
+ launchOptions.proxy = {
183
+ server: cliOptions.proxyServer
184
+ };
185
+ if (cliOptions.proxyBypass)
186
+ launchOptions.proxy.bypass = cliOptions.proxyBypass;
187
+ }
188
+ if (cliOptions.device && cliOptions.cdpEndpoint)
189
+ throw new Error("Device emulation is not supported with cdpEndpoint.");
190
+ const contextOptions = cliOptions.device ? import_playwright_core.devices[cliOptions.device] : {};
191
+ if (cliOptions.storageState)
192
+ contextOptions.storageState = cliOptions.storageState;
193
+ if (cliOptions.userAgent)
194
+ contextOptions.userAgent = cliOptions.userAgent;
195
+ if (cliOptions.viewportSize)
196
+ contextOptions.viewport = cliOptions.viewportSize;
197
+ if (cliOptions.ignoreHttpsErrors)
198
+ contextOptions.ignoreHTTPSErrors = true;
199
+ if (cliOptions.blockServiceWorkers)
200
+ contextOptions.serviceWorkers = "block";
201
+ if (cliOptions.grantPermissions)
202
+ contextOptions.permissions = cliOptions.grantPermissions;
203
+ const config = {
204
+ browser: {
205
+ browserName,
206
+ isolated: cliOptions.isolated,
207
+ userDataDir: cliOptions.userDataDir,
208
+ launchOptions,
209
+ contextOptions,
210
+ cdpEndpoint: cliOptions.cdpEndpoint,
211
+ cdpHeaders: cliOptions.cdpHeader,
212
+ cdpTimeout: cliOptions.cdpTimeout,
213
+ initPage: cliOptions.initPage,
214
+ initScript: cliOptions.initScript
215
+ },
216
+ extension: cliOptions.extension,
217
+ server: {
218
+ port: cliOptions.port,
219
+ host: cliOptions.host,
220
+ allowedHosts: cliOptions.allowedHosts
221
+ },
222
+ capabilities: cliOptions.caps,
223
+ console: {
224
+ level: cliOptions.consoleLevel
225
+ },
226
+ network: {
227
+ allowedOrigins: cliOptions.allowedOrigins,
228
+ blockedOrigins: cliOptions.blockedOrigins
229
+ },
230
+ allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess,
231
+ codegen: cliOptions.codegen,
232
+ saveSession: cliOptions.saveSession,
233
+ saveTrace: cliOptions.saveTrace,
234
+ saveVideo: cliOptions.saveVideo,
235
+ secrets: cliOptions.secrets,
236
+ sharedBrowserContext: cliOptions.sharedBrowserContext,
237
+ outputMode: cliOptions.outputMode,
238
+ outputDir: cliOptions.outputDir,
239
+ imageResponses: cliOptions.imageResponses,
240
+ testIdAttribute: cliOptions.testIdAttribute,
241
+ timeouts: {
242
+ action: cliOptions.timeoutAction,
243
+ navigation: cliOptions.timeoutNavigation
244
+ },
245
+ snapshot: {
246
+ mode: cliOptions.snapshotMode,
247
+ engine: cliOptions.snapshotEngine,
248
+ domSerializerPath: cliOptions.domSerializerPath ?? cliOptions.domSerializer,
249
+ domFallbackToAria: cliOptions.domFallbackToAria,
250
+ domNonstrict: cliOptions.domNonstrict
251
+ }
252
+ };
253
+ return { ...config, configFile: cliOptions.config };
254
+ }
255
+ function configFromEnv() {
256
+ const options = {};
257
+ options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);
258
+ options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
259
+ options.allowUnrestrictedFileAccess = envToBoolean(process.env.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS);
260
+ options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
261
+ options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
262
+ options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
263
+ options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
264
+ options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
265
+ options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});
266
+ options.cdpTimeout = numberParser(process.env.PLAYWRIGHT_MCP_CDP_TIMEOUT);
267
+ options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
268
+ if (process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL)
269
+ options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL);
270
+ options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
271
+ options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
272
+ options.extension = envToBoolean(process.env.PLAYWRIGHT_MCP_EXTENSION);
273
+ options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
274
+ options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
275
+ options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
276
+ options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
277
+ const initPage = envToString(process.env.PLAYWRIGHT_MCP_INIT_PAGE);
278
+ if (initPage)
279
+ options.initPage = [initPage];
280
+ const initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);
281
+ if (initScript)
282
+ options.initScript = [initScript];
283
+ options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
284
+ if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES)
285
+ options.imageResponses = enumParser("--image-responses", ["allow", "omit"], process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES);
286
+ options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
287
+ options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
288
+ options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);
289
+ options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);
290
+ options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);
291
+ options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);
292
+ options.saveVideo = resolutionParser("--save-video", process.env.PLAYWRIGHT_MCP_SAVE_VIDEO);
293
+ options.secrets = dotenvFileLoader(process.env.PLAYWRIGHT_MCP_SECRETS_FILE);
294
+ options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
295
+ options.snapshotEngine = process.env.PLAYWRIGHT_MCP_SNAPSHOT_ENGINE ? enumParser("--snapshot-engine", ["aria", "dom"], process.env.PLAYWRIGHT_MCP_SNAPSHOT_ENGINE) : void 0;
296
+ options.testIdAttribute = envToString(process.env.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE);
297
+ options.timeoutAction = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_ACTION);
298
+ options.timeoutNavigation = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION);
299
+ options.domSerializerPath = envToString(process.env.PLAYWRIGHT_MCP_DOM_SERIALIZER_PATH);
300
+ options.domFallbackToAria = envToBoolean(process.env.PLAYWRIGHT_MCP_DOM_FALLBACK_TO_ARIA);
301
+ options.domNonstrict = envToBoolean(process.env.PLAYWRIGHT_MCP_DOM_NONSTRICT);
302
+ options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
303
+ options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);
304
+ options.viewportSize = resolutionParser("--viewport-size", process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
305
+ return configFromCLIOptions(options);
306
+ }
307
+ async function loadConfig(configFile) {
308
+ if (!configFile)
309
+ return {};
310
+ if (configFile.endsWith(".ini"))
311
+ return (0, import_configIni.configFromIniFile)(configFile);
312
+ try {
313
+ return JSON.parse(await import_fs.default.promises.readFile(configFile, "utf8"));
314
+ } catch {
315
+ return (0, import_configIni.configFromIniFile)(configFile);
316
+ }
317
+ }
318
+ function workspaceDir(clientInfo) {
319
+ return import_path.default.resolve((0, import_server2.firstRootPath)(clientInfo) ?? process.cwd());
320
+ }
321
+ async function workspaceFile(config, clientInfo, fileName, perCallWorkspaceDir) {
322
+ const workspace = perCallWorkspaceDir ?? workspaceDir(clientInfo);
323
+ const resolvedName = import_path.default.resolve(workspace, fileName);
324
+ await checkFile(config, clientInfo, resolvedName, { origin: "code" });
325
+ return resolvedName;
326
+ }
327
+ function outputDir(config, clientInfo) {
328
+ if (config.outputDir)
329
+ return import_path.default.resolve(config.outputDir);
330
+ const rootPath = (0, import_server2.firstRootPath)(clientInfo);
331
+ if (rootPath)
332
+ return import_path.default.resolve(rootPath, config.skillMode ? ".playwright-cli" : ".playwright-mcp");
333
+ const tmpDir = process.env.PW_TMPDIR_FOR_TEST ?? import_os.default.tmpdir();
334
+ return import_path.default.resolve(tmpDir, "playwright-mcp-output", String(clientInfo.timestamp));
335
+ }
336
+ async function outputFile(config, clientInfo, fileName, options) {
337
+ const resolvedFile = import_path.default.resolve(outputDir(config, clientInfo), fileName);
338
+ await checkFile(config, clientInfo, resolvedFile, options);
339
+ await import_fs.default.promises.mkdir(import_path.default.dirname(resolvedFile), { recursive: true });
340
+ (0, import_utilsBundle.debug)("pw:mcp:file")(resolvedFile);
341
+ return resolvedFile;
342
+ }
343
+ async function checkFile(config, clientInfo, resolvedFilename, options) {
344
+ if (options.origin === "code")
345
+ return;
346
+ const output = outputDir(config, clientInfo);
347
+ const workspace = workspaceDir(clientInfo);
348
+ if (!resolvedFilename.startsWith(output) && !resolvedFilename.startsWith(workspace))
349
+ throw new Error(`Resolved file path ${resolvedFilename} is outside of the output directory ${output} and workspace directory ${workspace}. Use relative file names to stay within the output directory.`);
350
+ }
351
+ function pickDefined(obj) {
352
+ return Object.fromEntries(
353
+ Object.entries(obj ?? {}).filter(([_, v]) => v !== void 0)
354
+ );
355
+ }
356
+ function mergeConfig(base, overrides) {
357
+ const browser = {
358
+ ...pickDefined(base.browser),
359
+ ...pickDefined(overrides.browser),
360
+ browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? "chromium",
361
+ isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,
362
+ launchOptions: {
363
+ ...pickDefined(base.browser?.launchOptions),
364
+ ...pickDefined(overrides.browser?.launchOptions),
365
+ ...{ assistantMode: true }
366
+ },
367
+ contextOptions: {
368
+ ...pickDefined(base.browser?.contextOptions),
369
+ ...pickDefined(overrides.browser?.contextOptions)
370
+ }
371
+ };
372
+ if (browser.browserName !== "chromium" && browser.launchOptions)
373
+ delete browser.launchOptions.channel;
374
+ return {
375
+ ...pickDefined(base),
376
+ ...pickDefined(overrides),
377
+ browser,
378
+ console: {
379
+ ...pickDefined(base.console),
380
+ ...pickDefined(overrides.console)
381
+ },
382
+ network: {
383
+ ...pickDefined(base.network),
384
+ ...pickDefined(overrides.network)
385
+ },
386
+ server: {
387
+ ...pickDefined(base.server),
388
+ ...pickDefined(overrides.server)
389
+ },
390
+ snapshot: {
391
+ ...pickDefined(base.snapshot),
392
+ ...pickDefined(overrides.snapshot)
393
+ },
394
+ timeouts: {
395
+ ...pickDefined(base.timeouts),
396
+ ...pickDefined(overrides.timeouts)
397
+ }
398
+ };
399
+ }
400
+ function semicolonSeparatedList(value) {
401
+ if (!value)
402
+ return void 0;
403
+ return value.split(";").map((v) => v.trim());
404
+ }
405
+ function commaSeparatedList(value) {
406
+ if (!value)
407
+ return void 0;
408
+ return value.split(",").map((v) => v.trim());
409
+ }
410
+ function dotenvFileLoader(value) {
411
+ if (!value)
412
+ return void 0;
413
+ return import_utilsBundle.dotenv.parse(import_fs.default.readFileSync(value, "utf8"));
414
+ }
415
+ function numberParser(value) {
416
+ if (!value)
417
+ return void 0;
418
+ return +value;
419
+ }
420
+ function resolutionParser(name, value) {
421
+ if (!value)
422
+ return void 0;
423
+ if (value.includes("x")) {
424
+ const [width, height] = value.split("x").map((v) => +v);
425
+ if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
426
+ throw new Error(`Invalid resolution format: use ${name}="800x600"`);
427
+ return { width, height };
428
+ }
429
+ if (value.includes(",")) {
430
+ const [width, height] = value.split(",").map((v) => +v);
431
+ if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
432
+ throw new Error(`Invalid resolution format: use ${name}="800x600"`);
433
+ return { width, height };
434
+ }
435
+ throw new Error(`Invalid resolution format: use ${name}="800x600"`);
436
+ }
437
+ function headerParser(arg, previous) {
438
+ if (!arg)
439
+ return previous || {};
440
+ const result = previous || {};
441
+ const [name, value] = arg.split(":").map((v) => v.trim());
442
+ result[name] = value;
443
+ return result;
444
+ }
445
+ function enumParser(name, options, value) {
446
+ if (!options.includes(value))
447
+ throw new Error(`Invalid ${name}: ${value}. Valid values are: ${options.join(", ")}`);
448
+ return value;
449
+ }
450
+ function envToBoolean(value) {
451
+ if (value === "true" || value === "1")
452
+ return true;
453
+ if (value === "false" || value === "0")
454
+ return false;
455
+ return void 0;
456
+ }
457
+ function envToString(value) {
458
+ return value ? value.trim() : void 0;
459
+ }
460
+ function checkFfmpeg() {
461
+ try {
462
+ const executable = import_server.registry.findExecutable("ffmpeg");
463
+ return import_fs.default.existsSync(executable.executablePath());
464
+ } catch (error) {
465
+ return false;
466
+ }
467
+ }
468
+ // Annotate the CommonJS export names for ESM import in node:
469
+ 0 && (module.exports = {
470
+ commaSeparatedList,
471
+ configFromCLIOptions,
472
+ configFromEnv,
473
+ defaultConfig,
474
+ dotenvFileLoader,
475
+ enumParser,
476
+ headerParser,
477
+ loadConfig,
478
+ mergeConfig,
479
+ numberParser,
480
+ outputDir,
481
+ outputFile,
482
+ resolutionParser,
483
+ resolveCLIConfig,
484
+ resolveConfig,
485
+ semicolonSeparatedList,
486
+ validateConfig,
487
+ workspaceDir,
488
+ workspaceFile
489
+ });