@smoothdeploy/playwright 1.57.1 → 1.58.1-beta-1770383926000

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.

Potentially problematic release.


This version of @smoothdeploy/playwright might be problematic. Click here for more details.

Files changed (264) hide show
  1. package/ThirdPartyNotices.txt +1188 -65
  2. package/lib/agents/agentParser.js +89 -0
  3. package/lib/agents/generateAgents.js +27 -74
  4. package/lib/agents/generateAgents.js.map +7 -0
  5. package/lib/agents/playwright-test-planner.agent.md +1 -0
  6. package/lib/common/config.js +6 -4
  7. package/lib/common/config.js.map +7 -0
  8. package/lib/common/configLoader.js.map +7 -0
  9. package/lib/common/esmLoaderHost.js +2 -0
  10. package/lib/common/esmLoaderHost.js.map +7 -0
  11. package/lib/common/expectBundle.js +2 -17
  12. package/lib/common/expectBundle.js.map +7 -0
  13. package/lib/common/expectBundleImpl.js +132 -132
  14. package/lib/common/expectBundleImpl.js.map +7 -0
  15. package/lib/common/fixtures.js.map +7 -0
  16. package/lib/common/globals.js.map +7 -0
  17. package/lib/common/ipc.js.map +7 -0
  18. package/lib/common/poolBuilder.js.map +7 -0
  19. package/lib/common/process.js +28 -0
  20. package/lib/common/process.js.map +7 -0
  21. package/lib/common/suiteUtils.js.map +7 -0
  22. package/lib/common/test.js.map +7 -0
  23. package/lib/common/testLoader.js.map +7 -0
  24. package/lib/common/testType.js.map +7 -0
  25. package/lib/common/validators.js +10 -10
  26. package/lib/common/validators.js.map +7 -0
  27. package/lib/fsWatcher.js.map +7 -0
  28. package/lib/index.js +205 -12
  29. package/lib/index.js.map +7 -0
  30. package/lib/internalsForTest.js.map +7 -0
  31. package/lib/isomorphic/events.js.map +7 -0
  32. package/lib/isomorphic/folders.js.map +7 -0
  33. package/lib/isomorphic/stringInternPool.js.map +7 -0
  34. package/lib/isomorphic/teleReceiver.js +15 -2
  35. package/lib/isomorphic/teleReceiver.js.map +7 -0
  36. package/lib/isomorphic/teleSuiteUpdater.js +24 -4
  37. package/lib/isomorphic/teleSuiteUpdater.js.map +7 -0
  38. package/lib/isomorphic/testServerConnection.js.map +7 -0
  39. package/lib/isomorphic/testServerInterface.js.map +7 -0
  40. package/lib/isomorphic/testTree.js +13 -18
  41. package/lib/isomorphic/testTree.js.map +7 -0
  42. package/lib/isomorphic/types.d.js.map +7 -0
  43. package/lib/loader/loaderMain.js.map +7 -0
  44. package/lib/matchers/expect.js +2 -15
  45. package/lib/matchers/expect.js.map +7 -0
  46. package/lib/matchers/matcherHint.js +1 -44
  47. package/lib/matchers/matcherHint.js.map +7 -0
  48. package/lib/matchers/matchers.js +3 -2
  49. package/lib/matchers/matchers.js.map +7 -0
  50. package/lib/matchers/toBeTruthy.js +5 -3
  51. package/lib/matchers/toBeTruthy.js.map +7 -0
  52. package/lib/matchers/toEqual.js +4 -3
  53. package/lib/matchers/toEqual.js.map +7 -0
  54. package/lib/matchers/toHaveURL.js +5 -6
  55. package/lib/matchers/toHaveURL.js.map +7 -0
  56. package/lib/matchers/toMatchAriaSnapshot.js +5 -5
  57. package/lib/matchers/toMatchAriaSnapshot.js.map +7 -0
  58. package/lib/matchers/toMatchSnapshot.js +9 -8
  59. package/lib/matchers/toMatchSnapshot.js.map +7 -0
  60. package/lib/matchers/toMatchText.js +9 -9
  61. package/lib/matchers/toMatchText.js.map +7 -0
  62. package/lib/mcp/browser/actions.d.js.map +7 -0
  63. package/lib/mcp/browser/browserContextFactory.js +62 -29
  64. package/lib/mcp/browser/browserContextFactory.js.map +7 -0
  65. package/lib/mcp/browser/browserServerBackend.js +17 -9
  66. package/lib/mcp/browser/browserServerBackend.js.map +7 -0
  67. package/lib/mcp/browser/codegen.js.map +7 -0
  68. package/lib/mcp/browser/config.js +65 -12
  69. package/lib/mcp/browser/config.js.map +7 -0
  70. package/lib/mcp/browser/context.js +71 -94
  71. package/lib/mcp/browser/context.js.map +7 -0
  72. package/lib/mcp/browser/response.js +172 -131
  73. package/lib/mcp/browser/response.js.map +7 -0
  74. package/lib/mcp/browser/sessionLog.js +19 -104
  75. package/lib/mcp/browser/sessionLog.js.map +7 -0
  76. package/lib/mcp/browser/tab.js +92 -41
  77. package/lib/mcp/browser/tab.js.map +7 -0
  78. package/lib/mcp/browser/tools/common.js +8 -6
  79. package/lib/mcp/browser/tools/common.js.map +7 -0
  80. package/lib/mcp/browser/tools/console.js +7 -5
  81. package/lib/mcp/browser/tools/console.js.map +7 -0
  82. package/lib/mcp/browser/tools/dialogs.js +4 -4
  83. package/lib/mcp/browser/tools/dialogs.js.map +7 -0
  84. package/lib/mcp/browser/tools/evaluate.js +11 -19
  85. package/lib/mcp/browser/tools/evaluate.js.map +7 -0
  86. package/lib/mcp/browser/tools/files.js +3 -3
  87. package/lib/mcp/browser/tools/files.js.map +7 -0
  88. package/lib/mcp/browser/tools/form.js +9 -19
  89. package/lib/mcp/browser/tools/form.js.map +7 -0
  90. package/lib/mcp/browser/tools/install.js +6 -3
  91. package/lib/mcp/browser/tools/install.js.map +7 -0
  92. package/lib/mcp/browser/tools/keyboard.js +34 -11
  93. package/lib/mcp/browser/tools/keyboard.js.map +7 -0
  94. package/lib/mcp/browser/tools/mouse.js +11 -11
  95. package/lib/mcp/browser/tools/mouse.js.map +7 -0
  96. package/lib/mcp/browser/tools/navigate.js +14 -5
  97. package/lib/mcp/browser/tools/navigate.js.map +7 -0
  98. package/lib/mcp/browser/tools/network.js +20 -11
  99. package/lib/mcp/browser/tools/network.js.map +7 -0
  100. package/lib/mcp/browser/tools/open.js +57 -0
  101. package/lib/mcp/browser/tools/pdf.js +9 -19
  102. package/lib/mcp/browser/tools/pdf.js.map +7 -0
  103. package/lib/mcp/browser/tools/runCode.js +11 -8
  104. package/lib/mcp/browser/tools/runCode.js.map +7 -0
  105. package/lib/mcp/browser/tools/screenshot.js +16 -29
  106. package/lib/mcp/browser/tools/screenshot.js.map +7 -0
  107. package/lib/mcp/browser/tools/snapshot.js +21 -29
  108. package/lib/mcp/browser/tools/snapshot.js.map +7 -0
  109. package/lib/mcp/browser/tools/tabs.js +12 -12
  110. package/lib/mcp/browser/tools/tabs.js.map +7 -0
  111. package/lib/mcp/browser/tools/tool.js +2 -4
  112. package/lib/mcp/browser/tools/tool.js.map +7 -0
  113. package/lib/mcp/browser/tools/tracing.js +6 -6
  114. package/lib/mcp/browser/tools/tracing.js.map +7 -0
  115. package/lib/mcp/browser/tools/utils.js +49 -44
  116. package/lib/mcp/browser/tools/utils.js.map +7 -0
  117. package/lib/mcp/browser/tools/verify.js +24 -34
  118. package/lib/mcp/browser/tools/verify.js.map +7 -0
  119. package/lib/mcp/browser/tools/wait.js +6 -6
  120. package/lib/mcp/browser/tools/wait.js.map +7 -0
  121. package/lib/mcp/browser/tools.js +3 -1
  122. package/lib/mcp/browser/tools.js.map +7 -0
  123. package/lib/mcp/browser/watchdog.js.map +7 -0
  124. package/lib/mcp/config.d.js.map +7 -0
  125. package/lib/mcp/extension/cdpRelay.js +1 -1
  126. package/lib/mcp/extension/cdpRelay.js.map +7 -0
  127. package/lib/mcp/extension/extensionContextFactory.js +6 -5
  128. package/lib/mcp/extension/extensionContextFactory.js.map +7 -0
  129. package/lib/mcp/extension/protocol.js.map +7 -0
  130. package/lib/mcp/index.js.map +7 -0
  131. package/lib/mcp/log.js.map +7 -0
  132. package/lib/mcp/program.js +15 -20
  133. package/lib/mcp/program.js.map +7 -0
  134. package/lib/mcp/sdk/bundle.js.map +7 -0
  135. package/lib/mcp/sdk/exports.js +0 -2
  136. package/lib/mcp/sdk/exports.js.map +7 -0
  137. package/lib/mcp/sdk/http.js +20 -55
  138. package/lib/mcp/sdk/http.js.map +7 -0
  139. package/lib/mcp/sdk/inProcessTransport.js.map +7 -0
  140. package/lib/mcp/sdk/proxyBackend.js.map +7 -0
  141. package/lib/mcp/sdk/server.js +29 -4
  142. package/lib/mcp/sdk/server.js.map +7 -0
  143. package/lib/mcp/sdk/tool.js +2 -2
  144. package/lib/mcp/sdk/tool.js.map +7 -0
  145. package/lib/mcp/terminal/cli.js +296 -0
  146. package/lib/mcp/terminal/command.js +56 -0
  147. package/lib/mcp/terminal/commands.js +333 -0
  148. package/lib/mcp/terminal/daemon.js +129 -0
  149. package/lib/mcp/terminal/help.json +32 -0
  150. package/lib/mcp/terminal/helpGenerator.js +88 -0
  151. package/lib/mcp/terminal/socketConnection.js +80 -0
  152. package/lib/mcp/test/browserBackend.js +3 -13
  153. package/lib/mcp/test/browserBackend.js.map +7 -0
  154. package/lib/mcp/test/generatorTools.js +9 -9
  155. package/lib/mcp/test/generatorTools.js.map +7 -0
  156. package/lib/mcp/test/plannerTools.js +23 -22
  157. package/lib/mcp/test/plannerTools.js.map +7 -0
  158. package/lib/mcp/test/seed.js.map +7 -0
  159. package/lib/mcp/test/streams.js.map +7 -0
  160. package/lib/mcp/test/testBackend.js +6 -6
  161. package/lib/mcp/test/testBackend.js.map +7 -0
  162. package/lib/mcp/test/testContext.js +9 -3
  163. package/lib/mcp/test/testContext.js.map +7 -0
  164. package/lib/mcp/test/testTool.js.map +7 -0
  165. package/lib/mcp/test/testTools.js +12 -10
  166. package/lib/mcp/test/testTools.js.map +7 -0
  167. package/lib/mcpBundleImpl.js.map +7 -0
  168. package/lib/plugins/gitCommitInfoPlugin.js.map +7 -0
  169. package/lib/plugins/index.js.map +7 -0
  170. package/lib/plugins/webServerPlugin.js.map +7 -0
  171. package/lib/program.js +18 -4
  172. package/lib/program.js.map +7 -0
  173. package/lib/reporters/base.js +29 -4
  174. package/lib/reporters/base.js.map +7 -0
  175. package/lib/reporters/blob.js +3 -0
  176. package/lib/reporters/blob.js.map +7 -0
  177. package/lib/reporters/dot.js +17 -0
  178. package/lib/reporters/dot.js.map +7 -0
  179. package/lib/reporters/empty.js.map +7 -0
  180. package/lib/reporters/github.js.map +7 -0
  181. package/lib/reporters/html.js +13 -3
  182. package/lib/reporters/html.js.map +7 -0
  183. package/lib/reporters/internalReporter.js +6 -0
  184. package/lib/reporters/internalReporter.js.map +7 -0
  185. package/lib/reporters/json.js.map +7 -0
  186. package/lib/reporters/junit.js.map +7 -0
  187. package/lib/reporters/line.js +18 -0
  188. package/lib/reporters/line.js.map +7 -0
  189. package/lib/reporters/list.js +22 -0
  190. package/lib/reporters/list.js.map +7 -0
  191. package/lib/reporters/listModeReporter.js.map +7 -0
  192. package/lib/reporters/markdown.js.map +7 -0
  193. package/lib/reporters/merge.js +25 -8
  194. package/lib/reporters/merge.js.map +7 -0
  195. package/lib/reporters/multiplexer.js +8 -0
  196. package/lib/reporters/multiplexer.js.map +7 -0
  197. package/lib/reporters/reporterV2.js.map +7 -0
  198. package/lib/reporters/smoothdeploy.js +191 -0
  199. package/lib/reporters/teleEmitter.js +22 -4
  200. package/lib/reporters/teleEmitter.js.map +7 -0
  201. package/lib/reporters/versions/blobV1.js.map +7 -0
  202. package/lib/runner/dispatcher.js +20 -4
  203. package/lib/runner/dispatcher.js.map +7 -0
  204. package/lib/runner/failureTracker.js.map +7 -0
  205. package/lib/runner/lastRun.js.map +7 -0
  206. package/lib/runner/loadUtils.js +2 -2
  207. package/lib/runner/loadUtils.js.map +7 -0
  208. package/lib/runner/loaderHost.js.map +7 -0
  209. package/lib/runner/processHost.js +19 -0
  210. package/lib/runner/processHost.js.map +7 -0
  211. package/lib/runner/projectUtils.js +1 -1
  212. package/lib/runner/projectUtils.js.map +7 -0
  213. package/lib/runner/rebase.js.map +7 -0
  214. package/lib/runner/reporters.js +3 -1
  215. package/lib/runner/reporters.js.map +7 -0
  216. package/lib/runner/sigIntWatcher.js.map +7 -0
  217. package/lib/runner/storage.js +91 -0
  218. package/lib/runner/taskRunner.js.map +7 -0
  219. package/lib/runner/tasks.js.map +7 -0
  220. package/lib/runner/testGroups.js +14 -6
  221. package/lib/runner/testGroups.js.map +7 -0
  222. package/lib/runner/testRunner.js +13 -4
  223. package/lib/runner/testRunner.js.map +7 -0
  224. package/lib/runner/testServer.js +2 -2
  225. package/lib/runner/testServer.js.map +7 -0
  226. package/lib/runner/uiModeReporter.js.map +7 -0
  227. package/lib/runner/vcs.js.map +7 -0
  228. package/lib/runner/watchMode.js +2 -1
  229. package/lib/runner/watchMode.js.map +7 -0
  230. package/lib/runner/workerHost.js +6 -0
  231. package/lib/runner/workerHost.js.map +7 -0
  232. package/lib/third_party/pirates.js.map +7 -0
  233. package/lib/third_party/tsconfig-loader.js.map +7 -0
  234. package/lib/transform/babelBundle.js +3 -0
  235. package/lib/transform/babelBundle.js.map +7 -0
  236. package/lib/transform/babelBundleImpl.js +134 -134
  237. package/lib/transform/babelBundleImpl.js.map +7 -0
  238. package/lib/transform/compilationCache.js +2 -0
  239. package/lib/transform/compilationCache.js.map +7 -0
  240. package/lib/transform/esmLoader.js +10 -11
  241. package/lib/transform/esmLoader.js.map +7 -0
  242. package/lib/transform/md.js +221 -0
  243. package/lib/transform/portTransport.js.map +7 -0
  244. package/lib/transform/transform.js +18 -8
  245. package/lib/transform/transform.js.map +7 -0
  246. package/lib/util.js +3 -6
  247. package/lib/util.js.map +7 -0
  248. package/lib/utilsBundle.js +7 -0
  249. package/lib/utilsBundle.js.map +7 -0
  250. package/lib/utilsBundleImpl.js +51 -48
  251. package/lib/utilsBundleImpl.js.map +7 -0
  252. package/lib/worker/fixtureRunner.js +6 -2
  253. package/lib/worker/fixtureRunner.js.map +7 -0
  254. package/lib/worker/testInfo.js +39 -19
  255. package/lib/worker/testInfo.js.map +7 -0
  256. package/lib/worker/testTracing.js.map +7 -0
  257. package/lib/worker/timeoutManager.js.map +7 -0
  258. package/lib/worker/util.js.map +7 -0
  259. package/lib/worker/workerMain.js +17 -16
  260. package/lib/worker/workerMain.js.map +7 -0
  261. package/package.json +2 -2
  262. package/test.mjs +1 -0
  263. package/types/test.d.ts +26 -6
  264. package/types/testReporter.d.ts +1 -0
@@ -29,7 +29,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  var browserContextFactory_exports = {};
30
30
  __export(browserContextFactory_exports, {
31
31
  SharedContextFactory: () => SharedContextFactory,
32
- contextFactory: () => contextFactory
32
+ contextFactory: () => contextFactory,
33
+ identityBrowserContextFactory: () => identityBrowserContextFactory
33
34
  });
34
35
  module.exports = __toCommonJS(browserContextFactory_exports);
35
36
  var import_crypto = __toESM(require("crypto"));
@@ -53,16 +54,27 @@ function contextFactory(config) {
53
54
  return new IsolatedContextFactory(config);
54
55
  return new PersistentContextFactory(config);
55
56
  }
57
+ function identityBrowserContextFactory(browserContext) {
58
+ return {
59
+ createContext: async (clientInfo, abortSignal, options) => {
60
+ return {
61
+ browserContext,
62
+ close: async () => {
63
+ }
64
+ };
65
+ }
66
+ };
67
+ }
56
68
  class BaseContextFactory {
57
69
  constructor(name, config) {
58
70
  this._logName = name;
59
71
  this.config = config;
60
72
  }
61
- async _obtainBrowser(clientInfo) {
73
+ async _obtainBrowser(clientInfo, options) {
62
74
  if (this._browserPromise)
63
75
  return this._browserPromise;
64
76
  (0, import_log.testDebug)(`obtain browser (${this._logName})`);
65
- this._browserPromise = this._doObtainBrowser(clientInfo);
77
+ this._browserPromise = this._doObtainBrowser(clientInfo, options);
66
78
  void this._browserPromise.then((browser) => {
67
79
  browser.on("disconnected", () => {
68
80
  this._browserPromise = void 0;
@@ -72,28 +84,27 @@ class BaseContextFactory {
72
84
  });
73
85
  return this._browserPromise;
74
86
  }
75
- async _doObtainBrowser(clientInfo) {
87
+ async _doObtainBrowser(clientInfo, options) {
76
88
  throw new Error("Not implemented");
77
89
  }
78
- async createContext(clientInfo) {
90
+ async createContext(clientInfo, _, options) {
79
91
  (0, import_log.testDebug)(`create browser context (${this._logName})`);
80
- const browser = await this._obtainBrowser(clientInfo);
81
- const browserContext = await this._doCreateContext(browser);
92
+ const browser = await this._obtainBrowser(clientInfo, options);
93
+ const browserContext = await this._doCreateContext(browser, clientInfo);
82
94
  await addInitScript(browserContext, this.config.browser.initScript);
83
95
  return {
84
96
  browserContext,
85
- close: (afterClose) => this._closeBrowserContext(browserContext, browser, afterClose)
97
+ close: () => this._closeBrowserContext(browserContext, browser)
86
98
  };
87
99
  }
88
- async _doCreateContext(browser) {
100
+ async _doCreateContext(browser, clientInfo) {
89
101
  throw new Error("Not implemented");
90
102
  }
91
- async _closeBrowserContext(browserContext, browser, afterClose) {
103
+ async _closeBrowserContext(browserContext, browser) {
92
104
  (0, import_log.testDebug)(`close browser context (${this._logName})`);
93
105
  if (browser.contexts().length === 1)
94
106
  this._browserPromise = void 0;
95
107
  await browserContext.close().catch(import_log.logUnhandledError);
96
- await afterClose();
97
108
  if (browser.contexts().length === 0) {
98
109
  (0, import_log.testDebug)(`close browser (${this._logName})`);
99
110
  await browser.close().catch(import_log.logUnhandledError);
@@ -104,7 +115,7 @@ class IsolatedContextFactory extends BaseContextFactory {
104
115
  constructor(config) {
105
116
  super("isolated", config);
106
117
  }
107
- async _doObtainBrowser(clientInfo) {
118
+ async _doObtainBrowser(clientInfo, options) {
108
119
  await injectCdpPort(this.config.browser);
109
120
  const browserType = playwright[this.config.browser.browserName];
110
121
  const tracesDir = await computeTracesDir(this.config, clientInfo);
@@ -114,15 +125,16 @@ class IsolatedContextFactory extends BaseContextFactory {
114
125
  tracesDir,
115
126
  ...this.config.browser.launchOptions,
116
127
  handleSIGINT: false,
117
- handleSIGTERM: false
128
+ handleSIGTERM: false,
129
+ ...options.forceHeadless !== void 0 ? { headless: options.forceHeadless === "headless" } : {}
118
130
  }).catch((error) => {
119
131
  if (error.message.includes("Executable doesn't exist"))
120
132
  throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
121
133
  throw error;
122
134
  });
123
135
  }
124
- async _doCreateContext(browser) {
125
- return browser.newContext(this.config.browser.contextOptions);
136
+ async _doCreateContext(browser, clientInfo) {
137
+ return browser.newContext(await browserContextOptionsFromConfig(this.config, clientInfo));
126
138
  }
127
139
  }
128
140
  class CdpContextFactory extends BaseContextFactory {
@@ -130,7 +142,10 @@ class CdpContextFactory extends BaseContextFactory {
130
142
  super("cdp", config);
131
143
  }
132
144
  async _doObtainBrowser() {
133
- return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint, { headers: this.config.browser.cdpHeaders });
145
+ return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint, {
146
+ headers: this.config.browser.cdpHeaders,
147
+ timeout: this.config.browser.cdpTimeout
148
+ });
134
149
  }
135
150
  async _doCreateContext(browser) {
136
151
  return this.config.browser.isolated ? await browser.newContext() : browser.contexts()[0];
@@ -158,7 +173,7 @@ class PersistentContextFactory {
158
173
  this._userDataDirs = /* @__PURE__ */ new Set();
159
174
  this.config = config;
160
175
  }
161
- async createContext(clientInfo) {
176
+ async createContext(clientInfo, abortSignal, options) {
162
177
  await injectCdpPort(this.config.browser);
163
178
  (0, import_log.testDebug)("create browser context (persistent)");
164
179
  const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo);
@@ -172,23 +187,29 @@ class PersistentContextFactory {
172
187
  const launchOptions = {
173
188
  tracesDir,
174
189
  ...this.config.browser.launchOptions,
175
- ...this.config.browser.contextOptions,
190
+ ...await browserContextOptionsFromConfig(this.config, clientInfo),
176
191
  handleSIGINT: false,
177
192
  handleSIGTERM: false,
178
193
  ignoreDefaultArgs: [
179
194
  "--disable-extensions"
180
195
  ],
181
- assistantMode: true
196
+ assistantMode: true,
197
+ ...options.forceHeadless !== void 0 ? { headless: options.forceHeadless === "headless" } : {}
182
198
  };
183
199
  try {
184
200
  const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);
185
201
  await addInitScript(browserContext, this.config.browser.initScript);
186
- const close = (afterClose) => this._closeBrowserContext(browserContext, userDataDir, afterClose);
202
+ const close = () => this._closeBrowserContext(browserContext, userDataDir);
187
203
  return { browserContext, close };
188
204
  } catch (error) {
189
205
  if (error.message.includes("Executable doesn't exist"))
190
206
  throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
191
- if (error.message.includes("ProcessSingleton") || error.message.includes("Invalid URL")) {
207
+ if (error.message.includes("cannot open shared object file: No such file or directory")) {
208
+ const browserName = launchOptions.channel ?? this.config.browser.browserName;
209
+ throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`);
210
+ }
211
+ if (error.message.includes("ProcessSingleton") || // On Windows the process exits silently with code 21 when the profile is in use.
212
+ error.message.includes("exitCode=21")) {
192
213
  await new Promise((resolve) => setTimeout(resolve, 1e3));
193
214
  continue;
194
215
  }
@@ -197,13 +218,14 @@ class PersistentContextFactory {
197
218
  }
198
219
  throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
199
220
  }
200
- async _closeBrowserContext(browserContext, userDataDir, afterClose) {
221
+ async _closeBrowserContext(browserContext, userDataDir) {
201
222
  (0, import_log.testDebug)("close browser context (persistent)");
202
223
  (0, import_log.testDebug)("release user data dir", userDataDir);
203
224
  await browserContext.close().catch(() => {
204
225
  });
205
- await afterClose();
206
226
  this._userDataDirs.delete(userDataDir);
227
+ if (process.env.PWMCP_PROFILES_DIR_FOR_TEST && userDataDir.startsWith(process.env.PWMCP_PROFILES_DIR_FOR_TEST))
228
+ await import_fs.default.promises.rm(userDataDir, { recursive: true }).catch(import_log.logUnhandledError);
207
229
  (0, import_log.testDebug)("close browser context complete (persistent)");
208
230
  }
209
231
  async _createUserDataDir(clientInfo) {
@@ -257,10 +279,10 @@ class SharedContextFactory {
257
279
  constructor(baseFactory) {
258
280
  this._baseFactory = baseFactory;
259
281
  }
260
- async createContext(clientInfo, abortSignal, toolName) {
282
+ async createContext(clientInfo, abortSignal, options) {
261
283
  if (!this._contextPromise) {
262
284
  (0, import_log.testDebug)("create shared browser context");
263
- this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, toolName);
285
+ this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, options);
264
286
  }
265
287
  const { browserContext } = await this._contextPromise;
266
288
  (0, import_log.testDebug)(`shared context client connected`);
@@ -280,17 +302,28 @@ class SharedContextFactory {
280
302
  if (!contextPromise)
281
303
  return;
282
304
  const { close } = await contextPromise;
283
- await close(async () => {
284
- });
305
+ await close();
285
306
  }
286
307
  }
287
308
  async function computeTracesDir(config, clientInfo) {
288
309
  if (!config.saveTrace && !config.capabilities?.includes("tracing"))
289
310
  return;
290
- return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", reason: "Collecting trace" });
311
+ return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", title: "Collecting trace" });
312
+ }
313
+ async function browserContextOptionsFromConfig(config, clientInfo) {
314
+ const result = { ...config.browser.contextOptions };
315
+ if (config.saveVideo) {
316
+ const dir = await (0, import_config.outputFile)(config, clientInfo, `videos`, { origin: "code", title: "Saving video" });
317
+ result.recordVideo = {
318
+ dir,
319
+ size: config.saveVideo
320
+ };
321
+ }
322
+ return result;
291
323
  }
292
324
  // Annotate the CommonJS export names for ESM import in node:
293
325
  0 && (module.exports = {
294
326
  SharedContextFactory,
295
- contextFactory
327
+ contextFactory,
328
+ identityBrowserContextFactory
296
329
  });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/browser/browserContextFactory.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport crypto from 'crypto';\nimport fs from 'fs';\nimport net from 'net';\nimport path from 'path';\n\nimport * as playwright from 'playwright-core';\nimport { registryDirectory } from 'playwright-core/lib/server/registry/index';\nimport { startTraceViewerServer } from 'playwright-core/lib/server';\nimport { logUnhandledError, testDebug } from '../log';\nimport { outputFile } from './config';\nimport { firstRootPath } from '../sdk/server';\n\nimport type { FullConfig } from './config';\nimport type { LaunchOptions, BrowserContextOptions } from '../../../../playwright-core/src/client/types';\nimport type { ClientInfo } from '../sdk/server';\n\nexport function contextFactory(config: FullConfig): BrowserContextFactory {\n if (config.sharedBrowserContext)\n return SharedContextFactory.create(config);\n if (config.browser.remoteEndpoint)\n return new RemoteContextFactory(config);\n if (config.browser.cdpEndpoint)\n return new CdpContextFactory(config);\n if (config.browser.isolated)\n return new IsolatedContextFactory(config);\n return new PersistentContextFactory(config);\n}\n\nexport type BrowserContextFactoryResult = {\n browserContext: playwright.BrowserContext;\n close: (afterClose: () => Promise<void>) => Promise<void>;\n};\n\nexport interface BrowserContextFactory {\n createContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<BrowserContextFactoryResult>;\n}\n\nclass BaseContextFactory implements BrowserContextFactory {\n readonly config: FullConfig;\n private _logName: string;\n protected _browserPromise: Promise<playwright.Browser> | undefined;\n\n constructor(name: string, config: FullConfig) {\n this._logName = name;\n this.config = config;\n }\n\n protected async _obtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {\n if (this._browserPromise)\n return this._browserPromise;\n testDebug(`obtain browser (${this._logName})`);\n this._browserPromise = this._doObtainBrowser(clientInfo);\n void this._browserPromise.then(browser => {\n browser.on('disconnected', () => {\n this._browserPromise = undefined;\n });\n }).catch(() => {\n this._browserPromise = undefined;\n });\n return this._browserPromise;\n }\n\n protected async _doObtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {\n throw new Error('Not implemented');\n }\n\n async createContext(clientInfo: ClientInfo): Promise<BrowserContextFactoryResult> {\n testDebug(`create browser context (${this._logName})`);\n const browser = await this._obtainBrowser(clientInfo);\n const browserContext = await this._doCreateContext(browser);\n await addInitScript(browserContext, this.config.browser.initScript);\n return {\n browserContext,\n close: (afterClose: () => Promise<void>) => this._closeBrowserContext(browserContext, browser, afterClose)\n };\n }\n\n protected async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {\n throw new Error('Not implemented');\n }\n\n private async _closeBrowserContext(browserContext: playwright.BrowserContext, browser: playwright.Browser, afterClose: () => Promise<void>) {\n testDebug(`close browser context (${this._logName})`);\n if (browser.contexts().length === 1)\n this._browserPromise = undefined;\n await browserContext.close().catch(logUnhandledError);\n await afterClose();\n if (browser.contexts().length === 0) {\n testDebug(`close browser (${this._logName})`);\n await browser.close().catch(logUnhandledError);\n }\n }\n}\n\nclass IsolatedContextFactory extends BaseContextFactory {\n constructor(config: FullConfig) {\n super('isolated', config);\n }\n\n protected override async _doObtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {\n await injectCdpPort(this.config.browser);\n const browserType = playwright[this.config.browser.browserName];\n const tracesDir = await computeTracesDir(this.config, clientInfo);\n if (tracesDir && this.config.saveTrace)\n await startTraceServer(this.config, tracesDir);\n return browserType.launch({\n tracesDir,\n ...this.config.browser.launchOptions,\n handleSIGINT: false,\n handleSIGTERM: false,\n }).catch(error => {\n if (error.message.includes('Executable doesn\\'t exist'))\n throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);\n throw error;\n });\n }\n\n protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {\n return browser.newContext(this.config.browser.contextOptions);\n }\n}\n\nclass CdpContextFactory extends BaseContextFactory {\n constructor(config: FullConfig) {\n super('cdp', config);\n }\n\n protected override async _doObtainBrowser(): Promise<playwright.Browser> {\n return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint!, { headers: this.config.browser.cdpHeaders });\n }\n\n protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {\n return this.config.browser.isolated ? await browser.newContext() : browser.contexts()[0];\n }\n}\n\nclass RemoteContextFactory extends BaseContextFactory {\n constructor(config: FullConfig) {\n super('remote', config);\n }\n\n protected override async _doObtainBrowser(): Promise<playwright.Browser> {\n const url = new URL(this.config.browser.remoteEndpoint!);\n url.searchParams.set('browser', this.config.browser.browserName);\n if (this.config.browser.launchOptions)\n url.searchParams.set('launch-options', JSON.stringify(this.config.browser.launchOptions));\n return playwright[this.config.browser.browserName].connect(String(url));\n }\n\n protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {\n return browser.newContext();\n }\n}\n\nclass PersistentContextFactory implements BrowserContextFactory {\n readonly config: FullConfig;\n readonly name = 'persistent';\n readonly description = 'Create a new persistent browser context';\n\n private _userDataDirs = new Set<string>();\n\n constructor(config: FullConfig) {\n this.config = config;\n }\n\n async createContext(clientInfo: ClientInfo): Promise<BrowserContextFactoryResult> {\n await injectCdpPort(this.config.browser);\n testDebug('create browser context (persistent)');\n const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo);\n const tracesDir = await computeTracesDir(this.config, clientInfo);\n if (tracesDir && this.config.saveTrace)\n await startTraceServer(this.config, tracesDir);\n\n this._userDataDirs.add(userDataDir);\n testDebug('lock user data dir', userDataDir);\n\n const browserType = playwright[this.config.browser.browserName];\n for (let i = 0; i < 5; i++) {\n const launchOptions: LaunchOptions & BrowserContextOptions = {\n tracesDir,\n ...this.config.browser.launchOptions,\n ...this.config.browser.contextOptions,\n handleSIGINT: false,\n handleSIGTERM: false,\n ignoreDefaultArgs: [\n '--disable-extensions',\n ],\n assistantMode: true,\n };\n try {\n const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);\n await addInitScript(browserContext, this.config.browser.initScript);\n const close = (afterClose: () => Promise<void>) => this._closeBrowserContext(browserContext, userDataDir, afterClose);\n return { browserContext, close };\n } catch (error: any) {\n if (error.message.includes('Executable doesn\\'t exist'))\n throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);\n if (error.message.includes('ProcessSingleton') || error.message.includes('Invalid URL')) {\n // User data directory is already in use, try again.\n await new Promise(resolve => setTimeout(resolve, 1000));\n continue;\n }\n throw error;\n }\n }\n throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);\n }\n\n private async _closeBrowserContext(browserContext: playwright.BrowserContext, userDataDir: string, afterClose: () => Promise<void>) {\n testDebug('close browser context (persistent)');\n testDebug('release user data dir', userDataDir);\n await browserContext.close().catch(() => {});\n await afterClose();\n this._userDataDirs.delete(userDataDir);\n testDebug('close browser context complete (persistent)');\n }\n\n private async _createUserDataDir(clientInfo: ClientInfo) {\n const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? registryDirectory;\n const browserToken = this.config.browser.launchOptions?.channel ?? this.config.browser?.browserName;\n // Hesitant putting hundreds of files into the user's workspace, so using it for hashing instead.\n const rootPath = firstRootPath(clientInfo);\n const rootPathToken = rootPath ? `-${createHash(rootPath)}` : '';\n const result = path.join(dir, `mcp-${browserToken}${rootPathToken}`);\n await fs.promises.mkdir(result, { recursive: true });\n return result;\n }\n}\n\nasync function injectCdpPort(browserConfig: FullConfig['browser']) {\n if (browserConfig.browserName === 'chromium')\n (browserConfig.launchOptions as any).cdpPort = await findFreePort();\n}\n\nasync function findFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = net.createServer();\n server.listen(0, () => {\n const { port } = server.address() as net.AddressInfo;\n server.close(() => resolve(port));\n });\n server.on('error', reject);\n });\n}\n\nasync function startTraceServer(config: FullConfig, tracesDir: string): Promise<string | undefined> {\n if (!config.saveTrace)\n return;\n\n const server = await startTraceViewerServer();\n const urlPrefix = server.urlPrefix('human-readable');\n const url = urlPrefix + '/trace/index.html?trace=' + tracesDir + '/trace.json';\n // eslint-disable-next-line no-console\n console.error('\\nTrace viewer listening on ' + url);\n}\n\nfunction createHash(data: string): string {\n return crypto.createHash('sha256').update(data).digest('hex').slice(0, 7);\n}\n\nasync function addInitScript(browserContext: playwright.BrowserContext, initScript: string[] | undefined) {\n for (const scriptPath of initScript ?? [])\n await browserContext.addInitScript({ path: path.resolve(scriptPath) });\n}\n\nexport class SharedContextFactory implements BrowserContextFactory {\n private _contextPromise: Promise<BrowserContextFactoryResult> | undefined;\n private _baseFactory: BrowserContextFactory;\n private static _instance: SharedContextFactory | undefined;\n\n static create(config: FullConfig) {\n if (SharedContextFactory._instance)\n throw new Error('SharedContextFactory already exists');\n const baseConfig = { ...config, sharedBrowserContext: false };\n const baseFactory = contextFactory(baseConfig);\n SharedContextFactory._instance = new SharedContextFactory(baseFactory);\n return SharedContextFactory._instance;\n }\n\n private constructor(baseFactory: BrowserContextFactory) {\n this._baseFactory = baseFactory;\n }\n\n async createContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {\n if (!this._contextPromise) {\n testDebug('create shared browser context');\n this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, toolName);\n }\n\n const { browserContext } = await this._contextPromise;\n testDebug(`shared context client connected`);\n return {\n browserContext,\n close: async () => {\n testDebug(`shared context client disconnected`);\n },\n };\n }\n\n static async dispose() {\n await SharedContextFactory._instance?._dispose();\n }\n\n private async _dispose() {\n const contextPromise = this._contextPromise;\n this._contextPromise = undefined;\n if (!contextPromise)\n return;\n const { close } = await contextPromise;\n await close(async () => {});\n }\n}\n\nasync function computeTracesDir(config: FullConfig, clientInfo: ClientInfo): Promise<string | undefined> {\n if (!config.saveTrace && !config.capabilities?.includes('tracing'))\n return;\n return await outputFile(config, clientInfo, `traces`, { origin: 'code', reason: 'Collecting trace' });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_server", "path", "fs", "net", "crypto"]
7
+ }
@@ -47,24 +47,32 @@ class BrowserServerBackend {
47
47
  }
48
48
  async callTool(name, rawArguments) {
49
49
  const tool = this._tools.find((tool2) => tool2.schema.name === name);
50
- if (!tool)
51
- throw new Error(`Tool "${name}" not found`);
50
+ if (!tool) {
51
+ return {
52
+ content: [{ type: "text", text: `### Error
53
+ Tool "${name}" not found` }],
54
+ isError: true
55
+ };
56
+ }
52
57
  const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
53
58
  const context = this._context;
54
- const response = new import_response.Response(context, name, parsedArguments);
55
- response.logBegin();
59
+ const response = import_response.Response.create(context, name, parsedArguments);
56
60
  context.setRunningTool(name);
61
+ let responseObject;
57
62
  try {
58
63
  await tool.handle(context, parsedArguments, response);
59
- await response.finish();
60
- this._sessionLog?.logResponse(response);
64
+ responseObject = await response.build();
65
+ this._sessionLog?.logResponse(name, parsedArguments, responseObject);
61
66
  } catch (error) {
62
- response.addError(String(error));
67
+ return {
68
+ content: [{ type: "text", text: `### Error
69
+ ${String(error)}` }],
70
+ isError: true
71
+ };
63
72
  } finally {
64
73
  context.setRunningTool(void 0);
65
74
  }
66
- response.logEnd();
67
- return response.serialize();
75
+ return responseObject;
68
76
  }
69
77
  serverClosed() {
70
78
  void this._context?.dispose().catch(import_log.logUnhandledError);
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/browser/browserServerBackend.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FullConfig } from './config';\nimport { Context } from './context';\nimport { logUnhandledError } from '../log';\nimport { Response } from './response';\nimport { SessionLog } from './sessionLog';\nimport { filteredTools } from './tools';\nimport { toMcpTool } from '../sdk/tool';\n\nimport type { Tool } from './tools/tool';\nimport type { BrowserContextFactory } from './browserContextFactory';\nimport type * as mcpServer from '../sdk/server';\nimport type { ServerBackend } from '../sdk/server';\n\nexport class BrowserServerBackend implements ServerBackend {\n private _tools: Tool[];\n private _context: Context | undefined;\n private _sessionLog: SessionLog | undefined;\n private _config: FullConfig;\n private _browserContextFactory: BrowserContextFactory;\n\n constructor(config: FullConfig, factory: BrowserContextFactory) {\n this._config = config;\n this._browserContextFactory = factory;\n this._tools = filteredTools(config);\n }\n\n async initialize(clientInfo: mcpServer.ClientInfo): Promise<void> {\n this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, clientInfo) : undefined;\n this._context = new Context({\n config: this._config,\n browserContextFactory: this._browserContextFactory,\n sessionLog: this._sessionLog,\n clientInfo,\n });\n }\n\n async listTools(): Promise<mcpServer.Tool[]> {\n return this._tools.map(tool => toMcpTool(tool.schema));\n }\n\n async callTool(name: string, rawArguments: mcpServer.CallToolRequest['params']['arguments']) {\n const tool = this._tools.find(tool => tool.schema.name === name)!;\n if (!tool)\n throw new Error(`Tool \"${name}\" not found`);\n const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});\n const context = this._context!;\n const response = new Response(context, name, parsedArguments);\n response.logBegin();\n context.setRunningTool(name);\n try {\n await tool.handle(context, parsedArguments, response);\n await response.finish();\n this._sessionLog?.logResponse(response);\n } catch (error: any) {\n response.addError(String(error));\n } finally {\n context.setRunningTool(undefined);\n }\n response.logEnd();\n return response.serialize();\n }\n\n serverClosed() {\n void this._context?.dispose().catch(logUnhandledError);\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,qBAAwB;AACxB,iBAAkC;AAClC,sBAAyB;AACzB,wBAA2B;AAC3B,mBAA8B;AAC9B,kBAA0B;AAOnB,MAAM,qBAA8C;AAAA,EAOzD,YAAY,QAAoB,SAAgC;AAC9D,SAAK,UAAU;AACf,SAAK,yBAAyB;AAC9B,SAAK,aAAS,4BAAc,MAAM;AAAA,EACpC;AAAA,EAEA,MAAM,WAAW,YAAiD;AAChE,SAAK,cAAc,KAAK,QAAQ,cAAc,MAAM,6BAAW,OAAO,KAAK,SAAS,UAAU,IAAI;AAClG,SAAK,WAAW,IAAI,uBAAQ;AAAA,MAC1B,QAAQ,KAAK;AAAA,MACb,uBAAuB,KAAK;AAAA,MAC5B,YAAY,KAAK;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAuC;AAC3C,WAAO,KAAK,OAAO,IAAI,cAAQ,uBAAU,KAAK,MAAM,CAAC;AAAA,EACvD;AAAA,EAEA,MAAM,SAAS,MAAc,cAAgE;AAC3F,UAAM,OAAO,KAAK,OAAO,KAAK,CAAAA,UAAQA,MAAK,OAAO,SAAS,IAAI;AAC/D,QAAI,CAAC;AACH,YAAM,IAAI,MAAM,SAAS,IAAI,aAAa;AAC5C,UAAM,kBAAkB,KAAK,OAAO,YAAY,MAAM,gBAAgB,CAAC,CAAC;AACxE,UAAM,UAAU,KAAK;AACrB,UAAM,WAAW,IAAI,yBAAS,SAAS,MAAM,eAAe;AAC5D,aAAS,SAAS;AAClB,YAAQ,eAAe,IAAI;AAC3B,QAAI;AACF,YAAM,KAAK,OAAO,SAAS,iBAAiB,QAAQ;AACpD,YAAM,SAAS,OAAO;AACtB,WAAK,aAAa,YAAY,QAAQ;AAAA,IACxC,SAAS,OAAY;AACnB,eAAS,SAAS,OAAO,KAAK,CAAC;AAAA,IACjC,UAAE;AACA,cAAQ,eAAe,MAAS;AAAA,IAClC;AACA,aAAS,OAAO;AAChB,WAAO,SAAS,UAAU;AAAA,EAC5B;AAAA,EAEA,eAAe;AACb,SAAK,KAAK,UAAU,QAAQ,EAAE,MAAM,4BAAiB;AAAA,EACvD;AACF;",
6
+ "names": ["tool"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/browser/codegen.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// adapted from:\n// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/utils/isomorphic/stringUtils.ts\n// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/server/codegen/javascript.ts\n\n// NOTE: this function should not be used to escape any selectors.\nexport function escapeWithQuotes(text: string, char: string = '\\'') {\n const stringified = JSON.stringify(text);\n const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\\\\"/g, '\"');\n if (char === '\\'')\n return char + escapedText.replace(/[']/g, '\\\\\\'') + char;\n if (char === '\"')\n return char + escapedText.replace(/[\"]/g, '\\\\\"') + char;\n if (char === '`')\n return char + escapedText.replace(/[`]/g, '\\\\`') + char;\n throw new Error('Invalid escape char');\n}\n\nexport function quote(text: string) {\n return escapeWithQuotes(text, '\\'');\n}\n\nexport function formatObject(value: any, indent = ' ', mode: 'multiline' | 'oneline' = 'multiline'): string {\n if (typeof value === 'string')\n return quote(value);\n if (Array.isArray(value))\n return `[${value.map(o => formatObject(o)).join(', ')}]`;\n if (typeof value === 'object') {\n const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();\n if (!keys.length)\n return '{}';\n const tokens: string[] = [];\n for (const key of keys)\n tokens.push(`${key}: ${formatObject(value[key])}`);\n if (mode === 'multiline')\n return `{\\n${tokens.join(`,\\n${indent}`)}\\n}`;\n return `{ ${tokens.join(', ')} }`;\n }\n return String(value);\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,iBAAiB,MAAc,OAAe,KAAM;AAClE,QAAM,cAAc,KAAK,UAAU,IAAI;AACvC,QAAM,cAAc,YAAY,UAAU,GAAG,YAAY,SAAS,CAAC,EAAE,QAAQ,QAAQ,GAAG;AACxF,MAAI,SAAS;AACX,WAAO,OAAO,YAAY,QAAQ,QAAQ,KAAM,IAAI;AACtD,MAAI,SAAS;AACX,WAAO,OAAO,YAAY,QAAQ,QAAQ,KAAK,IAAI;AACrD,MAAI,SAAS;AACX,WAAO,OAAO,YAAY,QAAQ,QAAQ,KAAK,IAAI;AACrD,QAAM,IAAI,MAAM,qBAAqB;AACvC;AAEO,SAAS,MAAM,MAAc;AAClC,SAAO,iBAAiB,MAAM,GAAI;AACpC;AAEO,SAAS,aAAa,OAAY,SAAS,MAAM,OAAgC,aAAqB;AAC3G,MAAI,OAAO,UAAU;AACnB,WAAO,MAAM,KAAK;AACpB,MAAI,MAAM,QAAQ,KAAK;AACrB,WAAO,IAAI,MAAM,IAAI,OAAK,aAAa,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AACvD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,OAAO,OAAO,KAAK,KAAK,EAAE,OAAO,SAAO,MAAM,GAAG,MAAM,MAAS,EAAE,KAAK;AAC7E,QAAI,CAAC,KAAK;AACR,aAAO;AACT,UAAM,SAAmB,CAAC;AAC1B,eAAW,OAAO;AAChB,aAAO,KAAK,GAAG,GAAG,KAAK,aAAa,MAAM,GAAG,CAAC,CAAC,EAAE;AACnD,QAAI,SAAS;AACX,aAAO;AAAA,EAAM,OAAO,KAAK;AAAA,EAAM,MAAM,EAAE,CAAC;AAAA;AAC1C,WAAO,KAAK,OAAO,KAAK,IAAI,CAAC;AAAA,EAC/B;AACA,SAAO,OAAO,KAAK;AACrB;",
6
+ "names": []
7
+ }
@@ -32,13 +32,15 @@ __export(config_exports, {
32
32
  configFromCLIOptions: () => configFromCLIOptions,
33
33
  defaultConfig: () => defaultConfig,
34
34
  dotenvFileLoader: () => dotenvFileLoader,
35
+ enumParser: () => enumParser,
35
36
  headerParser: () => headerParser,
36
37
  numberParser: () => numberParser,
37
38
  outputDir: () => outputDir,
38
39
  outputFile: () => outputFile,
39
40
  resolutionParser: () => resolutionParser,
40
41
  resolveCLIConfig: () => resolveCLIConfig,
41
- resolveConfig: () => resolveConfig
42
+ resolveConfig: () => resolveConfig,
43
+ semicolonSeparatedList: () => semicolonSeparatedList
42
44
  });
43
45
  module.exports = __toCommonJS(config_exports);
44
46
  var import_fs = __toESM(require("fs"));
@@ -60,8 +62,19 @@ const defaultConfig = {
60
62
  viewport: null
61
63
  }
62
64
  },
65
+ console: {
66
+ level: "info"
67
+ },
68
+ network: {
69
+ allowedOrigins: void 0,
70
+ blockedOrigins: void 0
71
+ },
63
72
  server: {},
64
73
  saveTrace: false,
74
+ snapshot: {
75
+ mode: "incremental",
76
+ output: "stdout"
77
+ },
65
78
  timeouts: {
66
79
  action: 5e3,
67
80
  navigation: 6e4
@@ -88,6 +101,12 @@ async function validateConfig(config) {
88
101
  throw new Error(`Init script file does not exist: ${script}`);
89
102
  }
90
103
  }
104
+ if (config.browser.initPage) {
105
+ for (const page of config.browser.initPage) {
106
+ if (!await (0, import_util.fileExistsAsync)(page))
107
+ throw new Error(`Init page file does not exist: ${page}`);
108
+ }
109
+ }
91
110
  if (config.sharedBrowserContext && config.saveVideo)
92
111
  throw new Error("saveVideo is not supported when sharedBrowserContext is true");
93
112
  }
@@ -143,13 +162,6 @@ function configFromCLIOptions(cliOptions) {
143
162
  contextOptions.serviceWorkers = "block";
144
163
  if (cliOptions.grantPermissions)
145
164
  contextOptions.permissions = cliOptions.grantPermissions;
146
- if (cliOptions.saveVideo) {
147
- contextOptions.recordVideo = {
148
- // Videos are moved to output directory on saveAs.
149
- dir: tmpDir(),
150
- size: cliOptions.saveVideo
151
- };
152
- }
153
165
  const result = {
154
166
  browser: {
155
167
  browserName,
@@ -168,11 +180,22 @@ function configFromCLIOptions(cliOptions) {
168
180
  allowedHosts: cliOptions.allowedHosts
169
181
  },
170
182
  capabilities: cliOptions.caps,
183
+ console: {
184
+ level: cliOptions.consoleLevel
185
+ },
186
+ network: {
187
+ allowedOrigins: cliOptions.allowedOrigins,
188
+ blockedOrigins: cliOptions.blockedOrigins
189
+ },
190
+ allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess,
191
+ codegen: cliOptions.codegen,
171
192
  saveSession: cliOptions.saveSession,
172
193
  saveTrace: cliOptions.saveTrace,
173
194
  saveVideo: cliOptions.saveVideo,
174
195
  secrets: cliOptions.secrets,
175
196
  sharedBrowserContext: cliOptions.sharedBrowserContext,
197
+ snapshot: cliOptions.snapshotMode ? { mode: cliOptions.snapshotMode } : void 0,
198
+ outputMode: cliOptions.outputMode,
176
199
  outputDir: cliOptions.outputDir,
177
200
  imageResponses: cliOptions.imageResponses,
178
201
  testIdAttribute: cliOptions.testIdAttribute,
@@ -186,12 +209,17 @@ function configFromCLIOptions(cliOptions) {
186
209
  function configFromEnv() {
187
210
  const options = {};
188
211
  options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);
212
+ options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
213
+ options.allowUnrestrictedFileAccess = envToBoolean(process.env.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS);
214
+ options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
189
215
  options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
190
216
  options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
191
217
  options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
192
218
  options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
193
219
  options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});
194
220
  options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
221
+ if (process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL)
222
+ options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL);
195
223
  options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
196
224
  options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
197
225
  options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
@@ -205,8 +233,8 @@ function configFromEnv() {
205
233
  if (initScript)
206
234
  options.initScript = [initScript];
207
235
  options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
208
- if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === "omit")
209
- options.imageResponses = "omit";
236
+ if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES)
237
+ options.imageResponses = enumParser("--image-responses", ["allow", "omit"], process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES);
210
238
  options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
211
239
  options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
212
240
  options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);
@@ -242,7 +270,8 @@ function outputDir(config, clientInfo) {
242
270
  }
243
271
  async function outputFile(config, clientInfo, fileName, options) {
244
272
  const file = await resolveFile(config, clientInfo, fileName, options);
245
- (0, import_utilsBundle.debug)("pw:mcp:file")(options.reason, file);
273
+ await import_fs.default.promises.mkdir(import_path.default.dirname(file), { recursive: true });
274
+ (0, import_utilsBundle.debug)("pw:mcp:file")(options.title, file);
246
275
  return file;
247
276
  }
248
277
  async function resolveFile(config, clientInfo, fileName, options) {
@@ -285,16 +314,33 @@ function mergeConfig(base, overrides) {
285
314
  ...pickDefined(base),
286
315
  ...pickDefined(overrides),
287
316
  browser,
317
+ console: {
318
+ ...pickDefined(base.console),
319
+ ...pickDefined(overrides.console)
320
+ },
321
+ network: {
322
+ ...pickDefined(base.network),
323
+ ...pickDefined(overrides.network)
324
+ },
288
325
  server: {
289
326
  ...pickDefined(base.server),
290
327
  ...pickDefined(overrides.server)
291
328
  },
329
+ snapshot: {
330
+ ...pickDefined(base.snapshot),
331
+ ...pickDefined(overrides.snapshot)
332
+ },
292
333
  timeouts: {
293
334
  ...pickDefined(base.timeouts),
294
335
  ...pickDefined(overrides.timeouts)
295
336
  }
296
337
  };
297
338
  }
339
+ function semicolonSeparatedList(value) {
340
+ if (!value)
341
+ return void 0;
342
+ return value.split(";").map((v) => v.trim());
343
+ }
298
344
  function commaSeparatedList(value) {
299
345
  if (!value)
300
346
  return void 0;
@@ -335,6 +381,11 @@ function headerParser(arg, previous) {
335
381
  result[name] = value;
336
382
  return result;
337
383
  }
384
+ function enumParser(name, options, value) {
385
+ if (!options.includes(value))
386
+ throw new Error(`Invalid ${name}: ${value}. Valid values are: ${options.join(", ")}`);
387
+ return value;
388
+ }
338
389
  function envToBoolean(value) {
339
390
  if (value === "true" || value === "1")
340
391
  return true;
@@ -358,11 +409,13 @@ function sanitizeForFilePath(s) {
358
409
  configFromCLIOptions,
359
410
  defaultConfig,
360
411
  dotenvFileLoader,
412
+ enumParser,
361
413
  headerParser,
362
414
  numberParser,
363
415
  outputDir,
364
416
  outputFile,
365
417
  resolutionParser,
366
418
  resolveCLIConfig,
367
- resolveConfig
419
+ resolveConfig,
420
+ semicolonSeparatedList
368
421
  });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/browser/config.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport { devices } from 'playwright-core';\nimport { dotenv, debug } from 'playwright-core/lib/utilsBundle';\nimport { fileExistsAsync } from '../../util';\nimport { firstRootPath } from '../sdk/server';\n\nimport type * as playwright from '../../../types/test';\nimport type { Config, ToolCapability } from '../config';\nimport type { ClientInfo } from '../sdk/server';\n\ntype ViewportSize = { width: number; height: number };\n\nexport type CLIOptions = {\n allowedHosts?: string[];\n blockServiceWorkers?: boolean;\n browser?: string;\n caps?: string[];\n cdpEndpoint?: string;\n cdpHeader?: Record<string, string>;\n config?: string;\n device?: string;\n executablePath?: string;\n grantPermissions?: string[];\n headless?: boolean;\n host?: string;\n ignoreHttpsErrors?: boolean;\n initScript?: string[];\n initPage?: string[];\n isolated?: boolean;\n imageResponses?: 'allow' | 'omit';\n sandbox?: boolean;\n outputDir?: string;\n port?: number;\n proxyBypass?: string;\n proxyServer?: string;\n saveSession?: boolean;\n saveTrace?: boolean;\n saveVideo?: ViewportSize;\n secrets?: Record<string, string>;\n sharedBrowserContext?: boolean;\n storageState?: string;\n testIdAttribute?: string;\n timeoutAction?: number;\n timeoutNavigation?: number;\n userAgent?: string;\n userDataDir?: string;\n viewportSize?: ViewportSize;\n};\n\nexport const defaultConfig: FullConfig = {\n browser: {\n browserName: 'chromium',\n launchOptions: {\n channel: 'chrome',\n headless: os.platform() === 'linux' && !process.env.DISPLAY,\n chromiumSandbox: true,\n },\n contextOptions: {\n viewport: null,\n },\n },\n server: {},\n saveTrace: false,\n timeouts: {\n action: 5000,\n navigation: 60000,\n },\n};\n\ntype BrowserUserConfig = NonNullable<Config['browser']>;\n\nexport type FullConfig = Config & {\n browser: Omit<BrowserUserConfig, 'browserName'> & {\n browserName: 'chromium' | 'firefox' | 'webkit';\n launchOptions: NonNullable<BrowserUserConfig['launchOptions']>;\n contextOptions: NonNullable<BrowserUserConfig['contextOptions']>;\n },\n saveTrace: boolean;\n server: NonNullable<Config['server']>,\n timeouts: {\n action: number;\n navigation: number;\n },\n};\n\nexport async function resolveConfig(config: Config): Promise<FullConfig> {\n return mergeConfig(defaultConfig, config);\n}\n\nexport async function resolveCLIConfig(cliOptions: CLIOptions): Promise<FullConfig> {\n const configInFile = await loadConfig(cliOptions.config);\n const envOverrides = configFromEnv();\n const cliOverrides = configFromCLIOptions(cliOptions);\n let result = defaultConfig;\n result = mergeConfig(result, configInFile);\n result = mergeConfig(result, envOverrides);\n result = mergeConfig(result, cliOverrides);\n await validateConfig(result);\n return result;\n}\n\nasync function validateConfig(config: FullConfig): Promise<void> {\n if (config.browser.initScript) {\n for (const script of config.browser.initScript) {\n if (!await fileExistsAsync(script))\n throw new Error(`Init script file does not exist: ${script}`);\n }\n }\n if (config.sharedBrowserContext && config.saveVideo)\n throw new Error('saveVideo is not supported when sharedBrowserContext is true');\n}\n\nexport function configFromCLIOptions(cliOptions: CLIOptions): Config {\n let browserName: 'chromium' | 'firefox' | 'webkit' | undefined;\n let channel: string | undefined;\n switch (cliOptions.browser) {\n case 'chrome':\n case 'chrome-beta':\n case 'chrome-canary':\n case 'chrome-dev':\n case 'chromium':\n case 'msedge':\n case 'msedge-beta':\n case 'msedge-canary':\n case 'msedge-dev':\n browserName = 'chromium';\n channel = cliOptions.browser;\n break;\n case 'firefox':\n browserName = 'firefox';\n break;\n case 'webkit':\n browserName = 'webkit';\n break;\n }\n\n // Launch options\n const launchOptions: playwright.LaunchOptions = {\n channel,\n executablePath: cliOptions.executablePath,\n headless: cliOptions.headless,\n };\n\n // --no-sandbox was passed, disable the sandbox\n if (cliOptions.sandbox === false)\n launchOptions.chromiumSandbox = false;\n\n if (cliOptions.proxyServer) {\n launchOptions.proxy = {\n server: cliOptions.proxyServer\n };\n if (cliOptions.proxyBypass)\n launchOptions.proxy.bypass = cliOptions.proxyBypass;\n }\n\n if (cliOptions.device && cliOptions.cdpEndpoint)\n throw new Error('Device emulation is not supported with cdpEndpoint.');\n\n // Context options\n const contextOptions: playwright.BrowserContextOptions = cliOptions.device ? devices[cliOptions.device] : {};\n if (cliOptions.storageState)\n contextOptions.storageState = cliOptions.storageState;\n\n if (cliOptions.userAgent)\n contextOptions.userAgent = cliOptions.userAgent;\n\n if (cliOptions.viewportSize)\n contextOptions.viewport = cliOptions.viewportSize;\n\n if (cliOptions.ignoreHttpsErrors)\n contextOptions.ignoreHTTPSErrors = true;\n\n if (cliOptions.blockServiceWorkers)\n contextOptions.serviceWorkers = 'block';\n\n if (cliOptions.grantPermissions)\n contextOptions.permissions = cliOptions.grantPermissions;\n\n if (cliOptions.saveVideo) {\n contextOptions.recordVideo = {\n // Videos are moved to output directory on saveAs.\n dir: tmpDir(),\n size: cliOptions.saveVideo,\n };\n }\n\n const result: Config = {\n browser: {\n browserName,\n isolated: cliOptions.isolated,\n userDataDir: cliOptions.userDataDir,\n launchOptions,\n contextOptions,\n cdpEndpoint: cliOptions.cdpEndpoint,\n cdpHeaders: cliOptions.cdpHeader,\n initPage: cliOptions.initPage,\n initScript: cliOptions.initScript,\n },\n server: {\n port: cliOptions.port,\n host: cliOptions.host,\n allowedHosts: cliOptions.allowedHosts,\n },\n capabilities: cliOptions.caps as ToolCapability[],\n saveSession: cliOptions.saveSession,\n saveTrace: cliOptions.saveTrace,\n saveVideo: cliOptions.saveVideo,\n secrets: cliOptions.secrets,\n sharedBrowserContext: cliOptions.sharedBrowserContext,\n outputDir: cliOptions.outputDir,\n imageResponses: cliOptions.imageResponses,\n testIdAttribute: cliOptions.testIdAttribute,\n timeouts: {\n action: cliOptions.timeoutAction,\n navigation: cliOptions.timeoutNavigation,\n },\n };\n\n return result;\n}\n\nfunction configFromEnv(): Config {\n const options: CLIOptions = {};\n options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);\n options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);\n options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);\n options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);\n options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);\n options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});\n options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);\n options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);\n options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);\n options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);\n options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);\n options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);\n options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);\n const initPage = envToString(process.env.PLAYWRIGHT_MCP_INIT_PAGE);\n if (initPage)\n options.initPage = [initPage];\n const initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);\n if (initScript)\n options.initScript = [initScript];\n options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);\n if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')\n options.imageResponses = 'omit';\n options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);\n options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);\n options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);\n options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);\n options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);\n options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);\n options.saveVideo = resolutionParser('--save-video', process.env.PLAYWRIGHT_MCP_SAVE_VIDEO);\n options.secrets = dotenvFileLoader(process.env.PLAYWRIGHT_MCP_SECRETS_FILE);\n options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);\n options.testIdAttribute = envToString(process.env.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE);\n options.timeoutAction = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_ACTION);\n options.timeoutNavigation = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION);\n options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);\n options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);\n options.viewportSize = resolutionParser('--viewport-size', process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);\n return configFromCLIOptions(options);\n}\n\nasync function loadConfig(configFile: string | undefined): Promise<Config> {\n if (!configFile)\n return {};\n\n try {\n return JSON.parse(await fs.promises.readFile(configFile, 'utf8'));\n } catch (error) {\n throw new Error(`Failed to load config file: ${configFile}, ${error}`);\n }\n}\n\nfunction tmpDir(): string {\n return path.join(process.env.PW_TMPDIR_FOR_TEST ?? os.tmpdir(), 'playwright-mcp-output');\n}\n\nexport function outputDir(config: FullConfig, clientInfo: ClientInfo): string {\n const rootPath = firstRootPath(clientInfo);\n return config.outputDir\n ?? (rootPath ? path.join(rootPath, '.playwright-mcp') : undefined)\n ?? path.join(tmpDir(), String(clientInfo.timestamp));\n}\n\nexport async function outputFile(config: FullConfig, clientInfo: ClientInfo, fileName: string, options: { origin: 'code' | 'llm' | 'web', reason: string }): Promise<string> {\n const file = await resolveFile(config, clientInfo, fileName, options);\n debug('pw:mcp:file')(options.reason, file);\n return file;\n}\n\nasync function resolveFile(config: FullConfig, clientInfo: ClientInfo, fileName: string, options: { origin: 'code' | 'llm' | 'web' }): Promise<string> {\n const dir = outputDir(config, clientInfo);\n\n // Trust code.\n if (options.origin === 'code')\n return path.resolve(dir, fileName);\n\n // Trust llm to use valid characters in file names.\n if (options.origin === 'llm') {\n fileName = fileName.split('\\\\').join('/');\n const resolvedFile = path.resolve(dir, fileName);\n if (!resolvedFile.startsWith(path.resolve(dir) + path.sep))\n throw new Error(`Resolved file path ${resolvedFile} is outside of the output directory ${dir}. Use relative file names to stay within the output directory.`);\n return resolvedFile;\n }\n\n // Do not trust web, at all.\n return path.join(dir, sanitizeForFilePath(fileName));\n}\n\nfunction pickDefined<T extends object>(obj: T | undefined): Partial<T> {\n return Object.fromEntries(\n Object.entries(obj ?? {}).filter(([_, v]) => v !== undefined)\n ) as Partial<T>;\n}\n\nfunction mergeConfig(base: FullConfig, overrides: Config): FullConfig {\n const browser: FullConfig['browser'] = {\n ...pickDefined(base.browser),\n ...pickDefined(overrides.browser),\n browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? 'chromium',\n isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,\n launchOptions: {\n ...pickDefined(base.browser?.launchOptions),\n ...pickDefined(overrides.browser?.launchOptions),\n ...{ assistantMode: true },\n },\n contextOptions: {\n ...pickDefined(base.browser?.contextOptions),\n ...pickDefined(overrides.browser?.contextOptions),\n },\n };\n\n if (browser.browserName !== 'chromium' && browser.launchOptions)\n delete browser.launchOptions.channel;\n\n return {\n ...pickDefined(base),\n ...pickDefined(overrides),\n browser,\n server: {\n ...pickDefined(base.server),\n ...pickDefined(overrides.server),\n },\n timeouts: {\n ...pickDefined(base.timeouts),\n ...pickDefined(overrides.timeouts),\n },\n } as FullConfig;\n}\n\nexport function commaSeparatedList(value: string | undefined): string[] | undefined {\n if (!value)\n return undefined;\n return value.split(',').map(v => v.trim());\n}\n\nexport function dotenvFileLoader(value: string | undefined): Record<string, string> | undefined {\n if (!value)\n return undefined;\n return dotenv.parse(fs.readFileSync(value, 'utf8'));\n}\n\nexport function numberParser(value: string | undefined): number | undefined {\n if (!value)\n return undefined;\n return +value;\n}\n\nexport function resolutionParser(name: string, value: string | undefined): ViewportSize | undefined {\n if (!value)\n return undefined;\n if (value.includes('x')) {\n const [width, height] = value.split('x').map(v => +v);\n if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)\n throw new Error(`Invalid resolution format: use ${name}=\"800x600\"`);\n return { width, height };\n }\n\n // Legacy format\n if (value.includes(',')) {\n const [width, height] = value.split(',').map(v => +v);\n if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)\n throw new Error(`Invalid resolution format: use ${name}=\"800x600\"`);\n return { width, height };\n }\n\n throw new Error(`Invalid resolution format: use ${name}=\"800x600\"`);\n}\n\nexport function headerParser(arg: string | undefined, previous?: Record<string, string>): Record<string, string> {\n if (!arg)\n return previous || {};\n const result: Record<string, string> = previous || {};\n const [name, value] = arg.split(':').map(v => v.trim());\n result[name] = value;\n return result;\n}\n\nfunction envToBoolean(value: string | undefined): boolean | undefined {\n if (value === 'true' || value === '1')\n return true;\n if (value === 'false' || value === '0')\n return false;\n return undefined;\n}\n\nfunction envToString(value: string | undefined): string | undefined {\n return value ? value.trim() : undefined;\n}\n\nfunction sanitizeForFilePath(s: string) {\n const sanitize = (s: string) => s.replace(/[\\x00-\\x2C\\x2E-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7B-\\x7F]+/g, '-');\n const separator = s.lastIndexOf('.');\n if (separator === -1)\n return sanitize(s);\n return sanitize(s.substring(0, separator)) + '.' + sanitize(s.substring(separator + 1));\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["os", "fs", "path", "s"]
7
+ }