@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.
- package/lib/mcp/browser/browserContextFactory.js +1 -3
- package/lib/mcp/browser/tools/common.js +0 -6
- package/lib/mcp/program.js +6 -5
- package/lib/mcp/terminal/command.js +2 -2
- package/lib/mcp/terminal/commands.js +21 -10
- package/lib/mcp/terminal/daemon.js +2 -1
- package/lib/mcp/terminal/helpGenerator.js +19 -1
- package/lib/mcp/terminal/program.js +25 -16
- package/lib/mcpBundleImpl/index.js +27 -27
- package/package.json +1 -1
- package/lib/common/config.js +0 -281
- package/lib/common/configLoader.js +0 -344
- package/lib/common/esmLoaderHost.js +0 -104
- package/lib/common/expectBundle.js +0 -43
- package/lib/common/expectBundleImpl.js +0 -407
- package/lib/common/fixtures.js +0 -302
- package/lib/common/globals.js +0 -58
- package/lib/common/ipc.js +0 -60
- package/lib/common/poolBuilder.js +0 -85
- package/lib/common/process.js +0 -132
- package/lib/common/suiteUtils.js +0 -140
- package/lib/common/test.js +0 -322
- package/lib/common/testLoader.js +0 -101
- package/lib/common/testType.js +0 -298
- package/lib/common/validators.js +0 -68
- package/lib/mcp/browser/actions.d.js +0 -16
- package/lib/mcp/browser/codegen.js +0 -66
- package/lib/mcp/browser/processUtils.js +0 -102
- package/lib/mcp/browser/tools/script.js +0 -60
- package/lib/mcp/sdk/bundle.js +0 -75
- package/lib/mcp/sdk/mdb.js +0 -208
- package/lib/mcp/sdk/proxyBackend.js +0 -128
- package/lib/mcp/vscode/host.js +0 -187
- package/lib/mcp/vscode/main.js +0 -77
- package/lib/mcpBundleImpl.js +0 -41
- package/lib/third_party/pirates.js +0 -62
- package/lib/third_party/tsconfig-loader.js +0 -103
- package/lib/transform/babelBundle.js +0 -43
- package/lib/transform/babelBundleImpl.js +0 -461
- package/lib/transform/babelHighlightUtils.js +0 -63
- package/lib/transform/compilationCache.js +0 -272
- package/lib/transform/esmLoader.js +0 -103
- package/lib/transform/portTransport.js +0 -67
- package/lib/transform/transform.js +0 -296
- package/lib/utilsBundleImpl/index.js +0 -218
- package/lib/utilsBundleImpl.js +0 -103
|
@@ -304,9 +304,7 @@ class SharedContextFactory {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
async function computeTracesDir(config, clientInfo) {
|
|
307
|
-
|
|
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
|
-
});
|
package/lib/mcp/program.js
CHANGED
|
@@ -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
|
-
${
|
|
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.
|
|
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
|
|
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:
|
|
135
|
-
y:
|
|
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:
|
|
166
|
-
dy:
|
|
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:
|
|
321
|
-
h:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
59
|
+
playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open
|
|
60
60
|
|
|
61
|
-
to restart the session
|
|
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
|
|
410
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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"}
|
|
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");
|