@cotestdev/mcp_playwright 0.0.52 → 0.0.53

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 (45) hide show
  1. package/lib/mcp/browser/browserContextFactory.js +1 -3
  2. package/lib/mcp/program.js +2 -1
  3. package/lib/mcp/terminal/command.js +2 -2
  4. package/lib/mcp/terminal/commands.js +21 -10
  5. package/lib/mcp/terminal/daemon.js +2 -1
  6. package/lib/mcp/terminal/helpGenerator.js +19 -1
  7. package/lib/mcp/terminal/program.js +24 -12
  8. package/lib/mcpBundleImpl/index.js +27 -27
  9. package/package.json +1 -1
  10. package/lib/common/config.js +0 -281
  11. package/lib/common/configLoader.js +0 -344
  12. package/lib/common/esmLoaderHost.js +0 -104
  13. package/lib/common/expectBundle.js +0 -43
  14. package/lib/common/expectBundleImpl.js +0 -407
  15. package/lib/common/fixtures.js +0 -302
  16. package/lib/common/globals.js +0 -58
  17. package/lib/common/ipc.js +0 -60
  18. package/lib/common/poolBuilder.js +0 -85
  19. package/lib/common/process.js +0 -132
  20. package/lib/common/suiteUtils.js +0 -140
  21. package/lib/common/test.js +0 -322
  22. package/lib/common/testLoader.js +0 -101
  23. package/lib/common/testType.js +0 -298
  24. package/lib/common/validators.js +0 -68
  25. package/lib/mcp/browser/actions.d.js +0 -16
  26. package/lib/mcp/browser/codegen.js +0 -66
  27. package/lib/mcp/browser/processUtils.js +0 -102
  28. package/lib/mcp/browser/tools/script.js +0 -60
  29. package/lib/mcp/sdk/bundle.js +0 -75
  30. package/lib/mcp/sdk/mdb.js +0 -208
  31. package/lib/mcp/sdk/proxyBackend.js +0 -128
  32. package/lib/mcp/vscode/host.js +0 -187
  33. package/lib/mcp/vscode/main.js +0 -77
  34. package/lib/mcpBundleImpl.js +0 -41
  35. package/lib/third_party/pirates.js +0 -62
  36. package/lib/third_party/tsconfig-loader.js +0 -103
  37. package/lib/transform/babelBundle.js +0 -43
  38. package/lib/transform/babelBundleImpl.js +0 -461
  39. package/lib/transform/babelHighlightUtils.js +0 -63
  40. package/lib/transform/compilationCache.js +0 -272
  41. package/lib/transform/esmLoader.js +0 -103
  42. package/lib/transform/portTransport.js +0 -67
  43. package/lib/transform/transform.js +0 -296
  44. package/lib/utilsBundleImpl/index.js +0 -218
  45. 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 };
@@ -77,8 +77,9 @@ Please run the command below. It will install a local copy of ffmpeg and will no
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) {
@@ -405,17 +405,28 @@ const booleanOptions = [
405
405
  ];
406
406
  async function program(packageLocation) {
407
407
  const clientInfo = createClientInfo(packageLocation);
408
+ const help = require("./help.json");
408
409
  const argv = process.argv.slice(2);
409
- const args = require("minimist")(argv, { boolean: booleanOptions });
410
- for (const option of booleanOptions) {
410
+ const boolean = [...help.booleanOptions, ...booleanOptions];
411
+ const args = require("minimist")(argv, { boolean });
412
+ for (const [key, value] of Object.entries(args)) {
413
+ if (key !== "_" && typeof value !== "boolean")
414
+ args[key] = String(value);
415
+ }
416
+ for (let index = 0; index < args._.length; index++)
417
+ args._[index] = String(args._[index]);
418
+ for (const option of boolean) {
411
419
  if (!argv.includes(`--${option}`) && !argv.includes(`--no-${option}`))
412
420
  delete args[option];
421
+ if (argv.some((arg) => arg.startsWith(`--${option}=`) || arg.startsWith(`--no-${option}=`))) {
422
+ console.error(`boolean option '--${option}' should not be passed with '=value', use '--${option}' or '--no-${option}' instead`);
423
+ process.exit(1);
424
+ }
413
425
  }
414
426
  if (args.s) {
415
427
  args.session = args.s;
416
428
  delete args.s;
417
429
  }
418
- const help = require("./help.json");
419
430
  const commandName = args._?.[0];
420
431
  if (args.version || args.v) {
421
432
  console.log(clientInfo.version);
@@ -475,16 +486,16 @@ async function install(args) {
475
486
  const cwd = process.cwd();
476
487
  const playwrightDir = import_path.default.join(cwd, ".playwright");
477
488
  await import_fs.default.promises.mkdir(playwrightDir, { recursive: true });
478
- console.log(`Workspace initialized at ${cwd}`);
489
+ console.log(`\u2705 Workspace initialized at \`${cwd}\`.`);
479
490
  if (args.skills) {
480
491
  const skillSourceDir = import_path.default.join(__dirname, "../../skill");
481
492
  const skillDestDir = import_path.default.join(cwd, ".claude", "skills", "playwright-cli");
482
493
  if (!import_fs.default.existsSync(skillSourceDir)) {
483
- console.error("Skills source directory not found:", skillSourceDir);
494
+ console.error("\u274C Skills source directory not found:", skillSourceDir);
484
495
  process.exit(1);
485
496
  }
486
497
  await import_fs.default.promises.cp(skillSourceDir, skillDestDir, { recursive: true });
487
- console.log(`Skills installed to ${import_path.default.relative(cwd, skillDestDir)}`);
498
+ console.log(`\u2705 Skills installed to \`${import_path.default.relative(cwd, skillDestDir)}\`.`);
488
499
  }
489
500
  if (!args.config)
490
501
  await ensureConfiguredBrowserInstalled();
@@ -516,7 +527,7 @@ async function createDefaultConfig(channel) {
516
527
  }
517
528
  };
518
529
  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())}.`);
530
+ console.log(`\u2705 Created default config for ${channel} at ${import_path.default.relative(process.cwd(), defaultConfigFile())}.`);
520
531
  }
521
532
  async function findOrInstallDefaultBrowser() {
522
533
  const { registry } = await import("playwright-core/lib/server/registry/index");
@@ -525,7 +536,7 @@ async function findOrInstallDefaultBrowser() {
525
536
  const executable = registry.findExecutable(channel);
526
537
  if (!executable?.executablePath())
527
538
  continue;
528
- console.log(`Found ${channel}, will use it as the default browser.`);
539
+ console.log(`\u2705 Found ${channel}, will use it as the default browser.`);
529
540
  return channel;
530
541
  }
531
542
  const chromiumExecutable = registry.findExecutable("chromium");
@@ -670,9 +681,10 @@ async function renderSessionStatus(session) {
670
681
  const text = [];
671
682
  const config = session.config();
672
683
  const canConnect = await session.canConnect();
673
- const restartMarker = canConnect && !session.isCompatible() ? ` - v${config.version}, please reopen` : "";
674
684
  text.push(`- ${session.name}:`);
675
- text.push(` - status: ${canConnect ? "open" : "closed"}${restartMarker}`);
685
+ text.push(` - status: ${canConnect ? "open" : "closed"}`);
686
+ if (canConnect && !session.isCompatible())
687
+ text.push(` - version: v${config.version} [incompatible please re-open]`);
676
688
  if (config.resolvedConfig)
677
689
  text.push(...renderResolvedConfig(config.resolvedConfig));
678
690
  return text.join("\n");