@cotestdev/mcp_playwright 0.0.52 → 0.0.54

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 (46) hide show
  1. package/lib/mcp/browser/browserContextFactory.js +1 -3
  2. package/lib/mcp/browser/tools/common.js +0 -6
  3. package/lib/mcp/program.js +6 -5
  4. package/lib/mcp/terminal/command.js +2 -2
  5. package/lib/mcp/terminal/commands.js +21 -10
  6. package/lib/mcp/terminal/daemon.js +2 -1
  7. package/lib/mcp/terminal/helpGenerator.js +19 -1
  8. package/lib/mcp/terminal/program.js +25 -16
  9. package/lib/mcpBundleImpl/index.js +27 -27
  10. package/package.json +1 -1
  11. package/lib/common/config.js +0 -281
  12. package/lib/common/configLoader.js +0 -344
  13. package/lib/common/esmLoaderHost.js +0 -104
  14. package/lib/common/expectBundle.js +0 -43
  15. package/lib/common/expectBundleImpl.js +0 -407
  16. package/lib/common/fixtures.js +0 -302
  17. package/lib/common/globals.js +0 -58
  18. package/lib/common/ipc.js +0 -60
  19. package/lib/common/poolBuilder.js +0 -85
  20. package/lib/common/process.js +0 -132
  21. package/lib/common/suiteUtils.js +0 -140
  22. package/lib/common/test.js +0 -322
  23. package/lib/common/testLoader.js +0 -101
  24. package/lib/common/testType.js +0 -298
  25. package/lib/common/validators.js +0 -68
  26. package/lib/mcp/browser/actions.d.js +0 -16
  27. package/lib/mcp/browser/codegen.js +0 -66
  28. package/lib/mcp/browser/processUtils.js +0 -102
  29. package/lib/mcp/browser/tools/script.js +0 -60
  30. package/lib/mcp/sdk/bundle.js +0 -75
  31. package/lib/mcp/sdk/mdb.js +0 -208
  32. package/lib/mcp/sdk/proxyBackend.js +0 -128
  33. package/lib/mcp/vscode/host.js +0 -187
  34. package/lib/mcp/vscode/main.js +0 -77
  35. package/lib/mcpBundleImpl.js +0 -41
  36. package/lib/third_party/pirates.js +0 -62
  37. package/lib/third_party/tsconfig-loader.js +0 -103
  38. package/lib/transform/babelBundle.js +0 -43
  39. package/lib/transform/babelBundleImpl.js +0 -461
  40. package/lib/transform/babelHighlightUtils.js +0 -63
  41. package/lib/transform/compilationCache.js +0 -272
  42. package/lib/transform/esmLoader.js +0 -103
  43. package/lib/transform/portTransport.js +0 -67
  44. package/lib/transform/transform.js +0 -296
  45. package/lib/utilsBundleImpl/index.js +0 -218
  46. package/lib/utilsBundleImpl.js +0 -103
@@ -304,9 +304,7 @@ class SharedContextFactory {
304
304
  }
305
305
  }
306
306
  async function computeTracesDir(config, clientInfo) {
307
- if (!config.saveTrace && !config.capabilities?.includes("devtools"))
308
- return;
309
- return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code" });
307
+ return import_path.default.resolve((0, import_config.outputDir)(config, clientInfo), "traces");
310
308
  }
311
309
  async function browserContextOptionsFromConfig(config, clientInfo) {
312
310
  const result = { ...config.browser.contextOptions };
@@ -18,7 +18,6 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var common_exports = {};
20
20
  __export(common_exports, {
21
- baseSchema: () => import_schema2.baseSchema,
22
21
  default: () => common_default
23
22
  });
24
23
  module.exports = __toCommonJS(common_exports);
@@ -26,7 +25,6 @@ var import_mcpBundle = require("../../../mcpBundle");
26
25
  var import_tool = require("./tool");
27
26
  var import_response = require("../response");
28
27
  var import_schema = require("./schema");
29
- var import_schema2 = require("./schema");
30
28
  const close = (0, import_tool.defineTool)({
31
29
  capability: "core",
32
30
  schema: {
@@ -64,7 +62,3 @@ var common_default = [
64
62
  close,
65
63
  resize
66
64
  ];
67
- // Annotate the CommonJS export names for ESM import in node:
68
- 0 && (module.exports = {
69
- baseSchema
70
- });
@@ -67,18 +67,19 @@ Please run the command below. It will install a local copy of ffmpeg and will no
67
67
  const extensionContextFactory = new import_extensionContextFactory.ExtensionContextFactory(config.browser.launchOptions.channel || "chrome", config.browser.userDataDir, config.browser.launchOptions.executablePath, forceNewTab);
68
68
  if (config.sessionConfig) {
69
69
  const contextFactory2 = config.extension ? extensionContextFactory : browserContextFactory;
70
- console.log(`### Config`);
71
- console.log("```json");
72
- console.log(JSON.stringify(config, null, 2));
73
- console.log("```");
74
70
  try {
75
71
  const socketPath = await (0, import_daemon.startMcpDaemonServer)(config, contextFactory2);
72
+ console.log(`### Config`);
73
+ console.log("```json");
74
+ console.log(JSON.stringify(config, null, 2));
75
+ console.log("```");
76
76
  console.log(`### Success
77
77
  Daemon listening on ${socketPath}`);
78
78
  console.log("<EOF>");
79
79
  } catch (error) {
80
+ const message = process.env.PWDEBUGIMPL ? error.stack || error.message : error.message;
80
81
  console.log(`### Error
81
- ${error.message}`);
82
+ ${message}`);
82
83
  console.log("<EOF>");
83
84
  }
84
85
  return;
@@ -56,11 +56,11 @@ function zodParse(schema, data, type) {
56
56
  const label = type === "option" ? `'--${prop}' option` : `'${prop}' argument`;
57
57
  switch (issue.code) {
58
58
  case "invalid_type":
59
- return "error: " + label + ": " + issue.message.toLowerCase().replace(/invalid input:/, "").trim();
59
+ return "error: " + label + ": " + issue.message.replace(/Invalid input:/, "").trim();
60
60
  case "unrecognized_keys":
61
61
  return "error: unknown " + label;
62
62
  default:
63
- return "error: " + label + ": " + issue.message.toLowerCase();
63
+ return "error: " + label + ": " + issue.message;
64
64
  }
65
65
  });
66
66
  }).flat().join("\n"));
@@ -23,6 +23,17 @@ __export(commands_exports, {
23
23
  module.exports = __toCommonJS(commands_exports);
24
24
  var import_mcpBundle = require("../../mcpBundle");
25
25
  var import_command = require("./command");
26
+ const numberArg = import_mcpBundle.z.preprocess((val, ctx) => {
27
+ const number = Number(val);
28
+ if (Number.isNaN(number)) {
29
+ ctx.issues.push({
30
+ code: "custom",
31
+ message: `expected number, received '${val}'`,
32
+ input: val
33
+ });
34
+ }
35
+ return number;
36
+ }, import_mcpBundle.z.number());
26
37
  const open = (0, import_command.declareCommand)({
27
38
  name: "open",
28
39
  description: "Open the browser",
@@ -131,8 +142,8 @@ const mouseMove = (0, import_command.declareCommand)({
131
142
  description: "Move mouse to a given position",
132
143
  category: "mouse",
133
144
  args: import_mcpBundle.z.object({
134
- x: import_mcpBundle.z.number().describe("X coordinate"),
135
- y: import_mcpBundle.z.number().describe("Y coordinate")
145
+ x: numberArg.describe("X coordinate"),
146
+ y: numberArg.describe("Y coordinate")
136
147
  }),
137
148
  toolName: "browser_mouse_move_xy",
138
149
  toolParams: ({ x, y }) => ({ x, y })
@@ -162,8 +173,8 @@ const mouseWheel = (0, import_command.declareCommand)({
162
173
  description: "Scroll mouse wheel",
163
174
  category: "mouse",
164
175
  args: import_mcpBundle.z.object({
165
- dx: import_mcpBundle.z.number().describe("Y delta"),
166
- dy: import_mcpBundle.z.number().describe("X delta")
176
+ dx: numberArg.describe("Y delta"),
177
+ dy: numberArg.describe("X delta")
167
178
  }),
168
179
  toolName: "browser_mouse_wheel",
169
180
  toolParams: ({ dx: deltaY, dy: deltaX }) => ({ deltaY, deltaX })
@@ -317,8 +328,8 @@ const resize = (0, import_command.declareCommand)({
317
328
  description: "Resize the browser window",
318
329
  category: "core",
319
330
  args: import_mcpBundle.z.object({
320
- w: import_mcpBundle.z.number().describe("Width of the browser window"),
321
- h: import_mcpBundle.z.number().describe("Height of the browser window")
331
+ w: numberArg.describe("Width of the browser window"),
332
+ h: numberArg.describe("Height of the browser window")
322
333
  }),
323
334
  toolName: "browser_resize",
324
335
  toolParams: ({ w: width, h: height }) => ({ width, height })
@@ -356,7 +367,7 @@ const tabClose = (0, import_command.declareCommand)({
356
367
  description: "Close a browser tab",
357
368
  category: "tabs",
358
369
  args: import_mcpBundle.z.object({
359
- index: import_mcpBundle.z.number().optional().describe("Tab index. If omitted, current tab is closed.")
370
+ index: numberArg.optional().describe("Tab index. If omitted, current tab is closed.")
360
371
  }),
361
372
  toolName: "browser_tabs",
362
373
  toolParams: ({ index }) => ({ action: "close", index })
@@ -366,7 +377,7 @@ const tabSelect = (0, import_command.declareCommand)({
366
377
  description: "Select a browser tab",
367
378
  category: "tabs",
368
379
  args: import_mcpBundle.z.object({
369
- index: import_mcpBundle.z.number().describe("Tab index")
380
+ index: numberArg.describe("Tab index")
370
381
  }),
371
382
  toolName: "browser_tabs",
372
383
  toolParams: ({ index }) => ({ action: "select", index })
@@ -424,7 +435,7 @@ const cookieSet = (0, import_command.declareCommand)({
424
435
  options: import_mcpBundle.z.object({
425
436
  domain: import_mcpBundle.z.string().optional().describe("Cookie domain"),
426
437
  path: import_mcpBundle.z.string().optional().describe("Cookie path"),
427
- expires: import_mcpBundle.z.number().optional().describe("Cookie expiration as Unix timestamp"),
438
+ expires: numberArg.optional().describe("Cookie expiration as Unix timestamp"),
428
439
  httpOnly: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is HTTP only"),
429
440
  secure: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is secure"),
430
441
  sameSite: import_mcpBundle.z.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute")
@@ -552,7 +563,7 @@ const routeMock = (0, import_command.declareCommand)({
552
563
  pattern: import_mcpBundle.z.string().describe('URL pattern to match (e.g., "**/api/users")')
553
564
  }),
554
565
  options: import_mcpBundle.z.object({
555
- status: import_mcpBundle.z.number().optional().describe("HTTP status code (default: 200)"),
566
+ status: numberArg.optional().describe("HTTP status code (default: 200)"),
556
567
  body: import_mcpBundle.z.string().optional().describe("Response body (text or JSON string)"),
557
568
  ["content-type"]: import_mcpBundle.z.string().optional().describe("Content-Type header"),
558
569
  header: import_mcpBundle.z.union([import_mcpBundle.z.string(), import_mcpBundle.z.array(import_mcpBundle.z.string())]).optional().transform((v) => v ? Array.isArray(v) ? v : [v] : void 0).describe('Header to add in "Name: Value" format (repeatable)'),
@@ -118,7 +118,8 @@ async function startMcpDaemonServer(config, contextFactory) {
118
118
  }
119
119
  } catch (e) {
120
120
  daemonDebug("command failed", e);
121
- await connection.send({ id, error: e.message });
121
+ const error = process.env.PWDEBUGIMPL ? e.stack || e.message : e.message;
122
+ await connection.send({ id, error });
122
123
  }
123
124
  };
124
125
  });
@@ -23,6 +23,7 @@ __export(helpGenerator_exports, {
23
23
  generateReadme: () => generateReadme
24
24
  });
25
25
  module.exports = __toCommonJS(helpGenerator_exports);
26
+ var import_mcpBundle = require("../../mcpBundle");
26
27
  var import_commands = require("./commands");
27
28
  function commandArgs(command) {
28
29
  const args = [];
@@ -134,12 +135,29 @@ function generateReadmeEntry(command) {
134
135
  const suffix = "# " + command.description.toLowerCase();
135
136
  return formatWithGap(prefix, suffix, 40);
136
137
  }
138
+ function unwrapZodType(schema) {
139
+ if ("unwrap" in schema && typeof schema.unwrap === "function")
140
+ return unwrapZodType(schema.unwrap());
141
+ return schema;
142
+ }
137
143
  function generateHelpJSON() {
144
+ const booleanOptions = /* @__PURE__ */ new Set();
145
+ for (const command of Object.values(import_commands.commands)) {
146
+ if (!command.options)
147
+ continue;
148
+ const optionsShape = command.options.shape;
149
+ for (const [name, schema] of Object.entries(optionsShape)) {
150
+ const innerSchema = unwrapZodType(schema);
151
+ if (innerSchema instanceof import_mcpBundle.z.ZodBoolean)
152
+ booleanOptions.add(name);
153
+ }
154
+ }
138
155
  const help = {
139
156
  global: generateHelp(),
140
157
  commands: Object.fromEntries(
141
158
  Object.entries(import_commands.commands).map(([name, command]) => [name, generateCommandHelp(command)])
142
- )
159
+ ),
160
+ booleanOptions: [...booleanOptions]
143
161
  };
144
162
  return help;
145
163
  }
@@ -56,9 +56,9 @@ class Session {
56
56
  if (!this.isCompatible()) {
57
57
  throw new Error(`Client is v${this._clientInfo.version}, session '${this.name}' is v${this._config.version}. Run
58
58
 
59
- playwright-cli session-restart${this.name !== "default" ? ` ${this.name}` : ""}
59
+ playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open
60
60
 
61
- to restart the session daemon.`);
61
+ to restart the browser session.`);
62
62
  }
63
63
  }
64
64
  async run(args) {
@@ -398,24 +398,32 @@ const globalOptions = [
398
398
  const booleanOptions = [
399
399
  "all",
400
400
  "help",
401
- "version",
402
- "extension",
403
- "headed",
404
- "persistent"
401
+ "version"
405
402
  ];
406
403
  async function program(packageLocation) {
407
404
  const clientInfo = createClientInfo(packageLocation);
405
+ const help = require("./help.json");
408
406
  const argv = process.argv.slice(2);
409
- const args = require("minimist")(argv, { boolean: booleanOptions });
410
- for (const option of booleanOptions) {
407
+ const boolean = [...help.booleanOptions, ...booleanOptions];
408
+ const args = require("minimist")(argv, { boolean });
409
+ for (const [key, value] of Object.entries(args)) {
410
+ if (key !== "_" && typeof value !== "boolean")
411
+ args[key] = String(value);
412
+ }
413
+ for (let index = 0; index < args._.length; index++)
414
+ args._[index] = String(args._[index]);
415
+ for (const option of boolean) {
411
416
  if (!argv.includes(`--${option}`) && !argv.includes(`--no-${option}`))
412
417
  delete args[option];
418
+ if (argv.some((arg) => arg.startsWith(`--${option}=`) || arg.startsWith(`--no-${option}=`))) {
419
+ console.error(`boolean option '--${option}' should not be passed with '=value', use '--${option}' or '--no-${option}' instead`);
420
+ process.exit(1);
421
+ }
413
422
  }
414
423
  if (args.s) {
415
424
  args.session = args.s;
416
425
  delete args.s;
417
426
  }
418
- const help = require("./help.json");
419
427
  const commandName = args._?.[0];
420
428
  if (args.version || args.v) {
421
429
  console.log(clientInfo.version);
@@ -475,16 +483,16 @@ async function install(args) {
475
483
  const cwd = process.cwd();
476
484
  const playwrightDir = import_path.default.join(cwd, ".playwright");
477
485
  await import_fs.default.promises.mkdir(playwrightDir, { recursive: true });
478
- console.log(`Workspace initialized at ${cwd}`);
486
+ console.log(`\u2705 Workspace initialized at \`${cwd}\`.`);
479
487
  if (args.skills) {
480
488
  const skillSourceDir = import_path.default.join(__dirname, "../../skill");
481
489
  const skillDestDir = import_path.default.join(cwd, ".claude", "skills", "playwright-cli");
482
490
  if (!import_fs.default.existsSync(skillSourceDir)) {
483
- console.error("Skills source directory not found:", skillSourceDir);
491
+ console.error("\u274C Skills source directory not found:", skillSourceDir);
484
492
  process.exit(1);
485
493
  }
486
494
  await import_fs.default.promises.cp(skillSourceDir, skillDestDir, { recursive: true });
487
- console.log(`Skills installed to ${import_path.default.relative(cwd, skillDestDir)}`);
495
+ console.log(`\u2705 Skills installed to \`${import_path.default.relative(cwd, skillDestDir)}\`.`);
488
496
  }
489
497
  if (!args.config)
490
498
  await ensureConfiguredBrowserInstalled();
@@ -516,7 +524,7 @@ async function createDefaultConfig(channel) {
516
524
  }
517
525
  };
518
526
  await import_fs.default.promises.writeFile(defaultConfigFile(), JSON.stringify(config, null, 2));
519
- console.log(`Created default config for ${channel} at ${import_path.default.relative(process.cwd(), defaultConfigFile())}.`);
527
+ console.log(`\u2705 Created default config for ${channel} at ${import_path.default.relative(process.cwd(), defaultConfigFile())}.`);
520
528
  }
521
529
  async function findOrInstallDefaultBrowser() {
522
530
  const { registry } = await import("playwright-core/lib/server/registry/index");
@@ -525,7 +533,7 @@ async function findOrInstallDefaultBrowser() {
525
533
  const executable = registry.findExecutable(channel);
526
534
  if (!executable?.executablePath())
527
535
  continue;
528
- console.log(`Found ${channel}, will use it as the default browser.`);
536
+ console.log(`\u2705 Found ${channel}, will use it as the default browser.`);
529
537
  return channel;
530
538
  }
531
539
  const chromiumExecutable = registry.findExecutable("chromium");
@@ -670,9 +678,10 @@ async function renderSessionStatus(session) {
670
678
  const text = [];
671
679
  const config = session.config();
672
680
  const canConnect = await session.canConnect();
673
- const restartMarker = canConnect && !session.isCompatible() ? ` - v${config.version}, please reopen` : "";
674
681
  text.push(`- ${session.name}:`);
675
- text.push(` - status: ${canConnect ? "open" : "closed"}${restartMarker}`);
682
+ text.push(` - status: ${canConnect ? "open" : "closed"}`);
683
+ if (canConnect && !session.isCompatible())
684
+ text.push(` - version: v${config.version} [incompatible please re-open]`);
676
685
  if (config.resolvedConfig)
677
686
  text.push(...renderResolvedConfig(config.resolvedConfig));
678
687
  return text.join("\n");