@tontoko/fast-playwright-mcp 0.0.4
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/LICENSE +202 -0
- package/README.md +1047 -0
- package/cli.js +18 -0
- package/config.d.ts +124 -0
- package/index.d.ts +25 -0
- package/index.js +18 -0
- package/lib/actions.d.js +0 -0
- package/lib/batch/batch-executor.js +137 -0
- package/lib/browser-context-factory.js +252 -0
- package/lib/browser-server-backend.js +139 -0
- package/lib/config/constants.js +80 -0
- package/lib/config.js +405 -0
- package/lib/context.js +274 -0
- package/lib/diagnostics/common/diagnostic-base.js +63 -0
- package/lib/diagnostics/common/error-enrichment-utils.js +212 -0
- package/lib/diagnostics/common/index.js +56 -0
- package/lib/diagnostics/common/initialization-manager.js +210 -0
- package/lib/diagnostics/common/performance-tracker.js +132 -0
- package/lib/diagnostics/diagnostic-error.js +140 -0
- package/lib/diagnostics/diagnostic-level.js +123 -0
- package/lib/diagnostics/diagnostic-thresholds.js +347 -0
- package/lib/diagnostics/element-discovery.js +441 -0
- package/lib/diagnostics/enhanced-error-handler.js +376 -0
- package/lib/diagnostics/error-enrichment.js +157 -0
- package/lib/diagnostics/frame-reference-manager.js +179 -0
- package/lib/diagnostics/page-analyzer.js +639 -0
- package/lib/diagnostics/parallel-page-analyzer.js +129 -0
- package/lib/diagnostics/resource-manager.js +134 -0
- package/lib/diagnostics/smart-config.js +482 -0
- package/lib/diagnostics/smart-handle.js +118 -0
- package/lib/diagnostics/unified-system.js +717 -0
- package/lib/extension/cdp-relay.js +486 -0
- package/lib/extension/extension-context-factory.js +74 -0
- package/lib/extension/main.js +41 -0
- package/lib/file-utils.js +42 -0
- package/lib/generate-keys.js +75 -0
- package/lib/http-server.js +50 -0
- package/lib/in-process-client.js +64 -0
- package/lib/index.js +48 -0
- package/lib/javascript.js +90 -0
- package/lib/log.js +33 -0
- package/lib/loop/loop-claude.js +247 -0
- package/lib/loop/loop-open-ai.js +222 -0
- package/lib/loop/loop.js +174 -0
- package/lib/loop/main.js +46 -0
- package/lib/loopTools/context.js +76 -0
- package/lib/loopTools/main.js +65 -0
- package/lib/loopTools/perform.js +40 -0
- package/lib/loopTools/snapshot.js +37 -0
- package/lib/loopTools/tool.js +26 -0
- package/lib/manual-promise.js +125 -0
- package/lib/mcp/in-process-transport.js +91 -0
- package/lib/mcp/proxy-backend.js +127 -0
- package/lib/mcp/server.js +123 -0
- package/lib/mcp/transport.js +159 -0
- package/lib/package.js +28 -0
- package/lib/program.js +82 -0
- package/lib/response.js +493 -0
- package/lib/schemas/expectation.js +152 -0
- package/lib/session-log.js +210 -0
- package/lib/tab.js +417 -0
- package/lib/tools/base-tool-handler.js +141 -0
- package/lib/tools/batch-execute.js +150 -0
- package/lib/tools/common.js +65 -0
- package/lib/tools/console.js +60 -0
- package/lib/tools/diagnose/diagnose-analysis-runner.js +101 -0
- package/lib/tools/diagnose/diagnose-config-handler.js +130 -0
- package/lib/tools/diagnose/diagnose-report-builder.js +394 -0
- package/lib/tools/diagnose.js +147 -0
- package/lib/tools/dialogs.js +57 -0
- package/lib/tools/evaluate.js +67 -0
- package/lib/tools/files.js +53 -0
- package/lib/tools/find-elements.js +307 -0
- package/lib/tools/install.js +60 -0
- package/lib/tools/keyboard.js +93 -0
- package/lib/tools/mouse.js +110 -0
- package/lib/tools/navigate.js +82 -0
- package/lib/tools/network.js +50 -0
- package/lib/tools/pdf.js +46 -0
- package/lib/tools/screenshot.js +113 -0
- package/lib/tools/snapshot.js +158 -0
- package/lib/tools/tabs.js +97 -0
- package/lib/tools/tool.js +47 -0
- package/lib/tools/utils.js +131 -0
- package/lib/tools/wait.js +64 -0
- package/lib/tools.js +65 -0
- package/lib/types/batch.js +47 -0
- package/lib/types/diff.js +0 -0
- package/lib/types/performance.js +0 -0
- package/lib/types/threshold-base.js +0 -0
- package/lib/utils/array-utils.js +44 -0
- package/lib/utils/code-deduplication-utils.js +141 -0
- package/lib/utils/common-formatters.js +252 -0
- package/lib/utils/console-filter.js +64 -0
- package/lib/utils/diagnostic-report-utils.js +178 -0
- package/lib/utils/diff-formatter.js +126 -0
- package/lib/utils/disposable-manager.js +135 -0
- package/lib/utils/error-handler-middleware.js +77 -0
- package/lib/utils/image-processor.js +137 -0
- package/lib/utils/index.js +92 -0
- package/lib/utils/report-builder.js +189 -0
- package/lib/utils/request-logger.js +82 -0
- package/lib/utils/response-diff-detector.js +150 -0
- package/lib/utils/section-builder.js +62 -0
- package/lib/utils/tool-patterns.js +153 -0
- package/lib/utils.js +46 -0
- package/package.json +77 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/disposable-manager.ts
|
|
21
|
+
import { handleResourceDisposalError } from "./common-formatters.js";
|
|
22
|
+
|
|
23
|
+
class DisposableManager {
|
|
24
|
+
disposables = new Set;
|
|
25
|
+
category;
|
|
26
|
+
maxParallelDisposals;
|
|
27
|
+
disposed = false;
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.category = options.category ?? "DisposableManager";
|
|
30
|
+
this.maxParallelDisposals = options.maxParallelDisposals ?? 10;
|
|
31
|
+
}
|
|
32
|
+
register(resource) {
|
|
33
|
+
if (this.disposed) {
|
|
34
|
+
throw new Error("Cannot register resource: DisposableManager already disposed");
|
|
35
|
+
}
|
|
36
|
+
this.disposables.add(resource);
|
|
37
|
+
return resource;
|
|
38
|
+
}
|
|
39
|
+
unregister(resource) {
|
|
40
|
+
return this.disposables.delete(resource);
|
|
41
|
+
}
|
|
42
|
+
getRegisteredCount() {
|
|
43
|
+
return this.disposables.size;
|
|
44
|
+
}
|
|
45
|
+
isDisposed() {
|
|
46
|
+
return this.disposed;
|
|
47
|
+
}
|
|
48
|
+
async dispose() {
|
|
49
|
+
if (this.disposed) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.disposed = true;
|
|
53
|
+
const resources = Array.from(this.disposables);
|
|
54
|
+
if (resources.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await this.disposeInBatches(resources);
|
|
58
|
+
this.disposables.clear();
|
|
59
|
+
}
|
|
60
|
+
async disposeInBatches(resources) {
|
|
61
|
+
const batches = this.createBatches(resources, this.maxParallelDisposals);
|
|
62
|
+
await this.disposeBatchesRecursive(batches, 0);
|
|
63
|
+
}
|
|
64
|
+
async disposeBatchesRecursive(batches, index) {
|
|
65
|
+
if (index >= batches.length) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const batch = batches[index];
|
|
69
|
+
const disposePromises = batch.map((resource) => this.safeDisposeResource(resource));
|
|
70
|
+
const results = await Promise.allSettled(disposePromises);
|
|
71
|
+
this.logDisposalResults(results, batch.length);
|
|
72
|
+
await this.disposeBatchesRecursive(batches, index + 1);
|
|
73
|
+
}
|
|
74
|
+
createBatches(items, batchSize) {
|
|
75
|
+
const batches = [];
|
|
76
|
+
for (let i = 0;i < items.length; i += batchSize) {
|
|
77
|
+
batches.push(items.slice(i, i + batchSize));
|
|
78
|
+
}
|
|
79
|
+
return batches;
|
|
80
|
+
}
|
|
81
|
+
async safeDisposeResource(resource) {
|
|
82
|
+
try {
|
|
83
|
+
await resource.dispose();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
handleResourceDisposalError(error, `${this.category} resource`, () => {});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
logDisposalResults(results, batchSize) {
|
|
89
|
+
const failures = results.filter((result) => result.status === "rejected").length;
|
|
90
|
+
const _successes = batchSize - failures;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function createDisposableManager(category, options = {}) {
|
|
94
|
+
return new DisposableManager({ ...options, category });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class AutoDisposableWrapper {
|
|
98
|
+
disposed = false;
|
|
99
|
+
resource;
|
|
100
|
+
constructor(resource, timeoutMs) {
|
|
101
|
+
this.resource = resource;
|
|
102
|
+
if (timeoutMs !== undefined) {
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
if (!this.disposed) {
|
|
105
|
+
this.dispose();
|
|
106
|
+
}
|
|
107
|
+
}, timeoutMs);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
get() {
|
|
111
|
+
if (this.disposed) {
|
|
112
|
+
throw new Error("Cannot access disposed resource");
|
|
113
|
+
}
|
|
114
|
+
return this.resource;
|
|
115
|
+
}
|
|
116
|
+
isDisposed() {
|
|
117
|
+
return this.disposed;
|
|
118
|
+
}
|
|
119
|
+
async dispose() {
|
|
120
|
+
if (this.disposed) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.disposed = true;
|
|
124
|
+
await this.resource.dispose();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function createAutoDisposable(resource, timeoutMs) {
|
|
128
|
+
return new AutoDisposableWrapper(resource, timeoutMs);
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
createDisposableManager,
|
|
132
|
+
createAutoDisposable,
|
|
133
|
+
DisposableManager,
|
|
134
|
+
AutoDisposableWrapper
|
|
135
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/error-handler-middleware.ts
|
|
21
|
+
import debug from "debug";
|
|
22
|
+
import { getErrorMessage } from "./common-formatters.js";
|
|
23
|
+
var errorHandlerDebug = debug("pw:mcp:error-handler");
|
|
24
|
+
async function withErrorHandling(operation, context) {
|
|
25
|
+
try {
|
|
26
|
+
const result = await operation();
|
|
27
|
+
return result;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const message = getErrorMessage(error);
|
|
30
|
+
const contextInfo = context ? `[${context.component || "Unknown"}:${context.operationName || "operation"}]` : "[Unknown:operation]";
|
|
31
|
+
const enrichedError = new Error(`${contextInfo} ${message}`);
|
|
32
|
+
enrichedError.cause = error;
|
|
33
|
+
if (context?.onError) {
|
|
34
|
+
context.onError(enrichedError);
|
|
35
|
+
}
|
|
36
|
+
throw enrichedError;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function withErrorHandlingSync(operation, context) {
|
|
40
|
+
try {
|
|
41
|
+
return operation();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const message = getErrorMessage(error);
|
|
44
|
+
const contextInfo = context ? `[${context.component || "Unknown"}:${context.operationName || "operation"}]` : "[Unknown:operation]";
|
|
45
|
+
const enrichedError = new Error(`${contextInfo} ${message}`);
|
|
46
|
+
enrichedError.cause = error;
|
|
47
|
+
if (context?.onError) {
|
|
48
|
+
context.onError(enrichedError);
|
|
49
|
+
}
|
|
50
|
+
throw enrichedError;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function createBatchErrorHandler(componentName) {
|
|
54
|
+
return (error, stepName) => {
|
|
55
|
+
const message = getErrorMessage(error);
|
|
56
|
+
const step = stepName ? ` (step: ${stepName})` : "";
|
|
57
|
+
return new Error(`[${componentName}] Batch operation failed${step}: ${message}`);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createErrorReporter(component) {
|
|
61
|
+
return {
|
|
62
|
+
reportAndThrow(error, operation) {
|
|
63
|
+
const message = getErrorMessage(error);
|
|
64
|
+
throw new Error(`[${component}:${operation}] ${message}`);
|
|
65
|
+
},
|
|
66
|
+
reportWarning(error, operation) {
|
|
67
|
+
const message = getErrorMessage(error);
|
|
68
|
+
errorHandlerDebug(`[${component}:${operation}] Warning: ${message}`);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
withErrorHandlingSync,
|
|
74
|
+
withErrorHandling,
|
|
75
|
+
createErrorReporter,
|
|
76
|
+
createBatchErrorHandler
|
|
77
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/image-processor.ts
|
|
21
|
+
import sharp from "sharp";
|
|
22
|
+
function validateImageOptions(options) {
|
|
23
|
+
const errors = [];
|
|
24
|
+
if (options.quality !== undefined && (options.quality < 1 || options.quality > 100)) {
|
|
25
|
+
errors.push("quality must be between 1 and 100");
|
|
26
|
+
}
|
|
27
|
+
if (options.maxWidth !== undefined && options.maxWidth <= 0) {
|
|
28
|
+
errors.push("maxWidth must be greater than 0");
|
|
29
|
+
}
|
|
30
|
+
if (options.maxHeight !== undefined && options.maxHeight <= 0) {
|
|
31
|
+
errors.push("maxHeight must be greater than 0");
|
|
32
|
+
}
|
|
33
|
+
return errors;
|
|
34
|
+
}
|
|
35
|
+
async function processImageWithoutOptions(data, contentType) {
|
|
36
|
+
try {
|
|
37
|
+
const metadata = await sharp(data).metadata();
|
|
38
|
+
return {
|
|
39
|
+
data,
|
|
40
|
+
contentType,
|
|
41
|
+
originalSize: {
|
|
42
|
+
width: metadata.width || 0,
|
|
43
|
+
height: metadata.height || 0
|
|
44
|
+
},
|
|
45
|
+
processedSize: {
|
|
46
|
+
width: metadata.width || 0,
|
|
47
|
+
height: metadata.height || 0
|
|
48
|
+
},
|
|
49
|
+
compressionRatio: 1
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
return {
|
|
53
|
+
data,
|
|
54
|
+
contentType,
|
|
55
|
+
originalSize: { width: 0, height: 0 },
|
|
56
|
+
processedSize: { width: 0, height: 0 },
|
|
57
|
+
compressionRatio: 1
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function applyFormatConversion(image, format, quality) {
|
|
62
|
+
const outputContentType = `image/${format}`;
|
|
63
|
+
let processedImage = image;
|
|
64
|
+
switch (format) {
|
|
65
|
+
case "jpeg":
|
|
66
|
+
processedImage = image.jpeg({ quality: quality || 80 });
|
|
67
|
+
break;
|
|
68
|
+
case "png":
|
|
69
|
+
processedImage = image.png({ quality: quality || 100 });
|
|
70
|
+
break;
|
|
71
|
+
case "webp":
|
|
72
|
+
processedImage = image.webp({ quality: quality || 80 });
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
return { image: processedImage, contentType: outputContentType };
|
|
78
|
+
}
|
|
79
|
+
function applyQualityToExistingFormat(image, contentType, quality) {
|
|
80
|
+
if (contentType.includes("jpeg")) {
|
|
81
|
+
return image.jpeg({ quality });
|
|
82
|
+
}
|
|
83
|
+
if (contentType.includes("png")) {
|
|
84
|
+
return image.png({ quality });
|
|
85
|
+
}
|
|
86
|
+
if (contentType.includes("webp")) {
|
|
87
|
+
return image.webp({ quality });
|
|
88
|
+
}
|
|
89
|
+
return image;
|
|
90
|
+
}
|
|
91
|
+
async function processImage(data, contentType, options) {
|
|
92
|
+
if (!options || Object.keys(options).length === 0) {
|
|
93
|
+
return processImageWithoutOptions(data, contentType);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const originalMetadata = await sharp(data).metadata();
|
|
97
|
+
const originalSize = {
|
|
98
|
+
width: originalMetadata.width || 0,
|
|
99
|
+
height: originalMetadata.height || 0
|
|
100
|
+
};
|
|
101
|
+
let image = sharp(data);
|
|
102
|
+
if (options.maxWidth || options.maxHeight) {
|
|
103
|
+
image = image.resize(options.maxWidth, options.maxHeight, {
|
|
104
|
+
fit: "inside",
|
|
105
|
+
withoutEnlargement: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
let outputContentType = contentType;
|
|
109
|
+
if (options.format) {
|
|
110
|
+
const result = applyFormatConversion(image, options.format, options.quality);
|
|
111
|
+
image = result.image;
|
|
112
|
+
outputContentType = result.contentType;
|
|
113
|
+
} else if (options.quality) {
|
|
114
|
+
image = applyQualityToExistingFormat(image, contentType, options.quality);
|
|
115
|
+
}
|
|
116
|
+
const processedBuffer = await image.toBuffer();
|
|
117
|
+
const processedMetadata = await sharp(processedBuffer).metadata();
|
|
118
|
+
const processedSize = {
|
|
119
|
+
width: processedMetadata.width || originalSize.width,
|
|
120
|
+
height: processedMetadata.height || originalSize.height
|
|
121
|
+
};
|
|
122
|
+
const compressionRatio = processedBuffer.length / data.length;
|
|
123
|
+
return {
|
|
124
|
+
data: processedBuffer,
|
|
125
|
+
contentType: outputContentType,
|
|
126
|
+
originalSize,
|
|
127
|
+
processedSize,
|
|
128
|
+
compressionRatio
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw new Error(`Image processing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
validateImageOptions,
|
|
136
|
+
processImage
|
|
137
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/index.ts
|
|
21
|
+
import {
|
|
22
|
+
deduplicate,
|
|
23
|
+
deduplicateAndLimit,
|
|
24
|
+
filterTruthy,
|
|
25
|
+
joinFiltered,
|
|
26
|
+
limitItems
|
|
27
|
+
} from "./array-utils.js";
|
|
28
|
+
import {
|
|
29
|
+
ArrayBuilder,
|
|
30
|
+
formatDiagnosticKeyValue,
|
|
31
|
+
formatElementCounts,
|
|
32
|
+
formatError,
|
|
33
|
+
formatListItems,
|
|
34
|
+
getStatusIcon,
|
|
35
|
+
joinLines,
|
|
36
|
+
truncateAtWordBoundary
|
|
37
|
+
} from "./code-deduplication-utils.js";
|
|
38
|
+
import {
|
|
39
|
+
formatConfidence,
|
|
40
|
+
formatDiagnosticPair,
|
|
41
|
+
formatExecutionTime,
|
|
42
|
+
getErrorMessage,
|
|
43
|
+
handleResourceDisposalError
|
|
44
|
+
} from "./common-formatters.js";
|
|
45
|
+
import { createDisposableManager } from "./disposable-manager.js";
|
|
46
|
+
import {
|
|
47
|
+
addMouseOperationComment,
|
|
48
|
+
addNavigationComment,
|
|
49
|
+
addOperationComment,
|
|
50
|
+
addToolErrorContext,
|
|
51
|
+
applyCommonExpectations,
|
|
52
|
+
executeToolOperation,
|
|
53
|
+
resolveElementLocator,
|
|
54
|
+
setupToolResponse,
|
|
55
|
+
ToolPatterns,
|
|
56
|
+
validateAndResolveElement,
|
|
57
|
+
validateElementParams,
|
|
58
|
+
waitForToolCompletion
|
|
59
|
+
} from "./tool-patterns.js";
|
|
60
|
+
export {
|
|
61
|
+
waitForToolCompletion,
|
|
62
|
+
validateElementParams,
|
|
63
|
+
validateAndResolveElement,
|
|
64
|
+
truncateAtWordBoundary,
|
|
65
|
+
setupToolResponse,
|
|
66
|
+
resolveElementLocator,
|
|
67
|
+
limitItems,
|
|
68
|
+
joinLines,
|
|
69
|
+
joinFiltered,
|
|
70
|
+
handleResourceDisposalError,
|
|
71
|
+
getStatusIcon,
|
|
72
|
+
getErrorMessage,
|
|
73
|
+
formatListItems,
|
|
74
|
+
formatExecutionTime,
|
|
75
|
+
formatError,
|
|
76
|
+
formatElementCounts,
|
|
77
|
+
formatDiagnosticPair,
|
|
78
|
+
formatDiagnosticKeyValue,
|
|
79
|
+
formatConfidence,
|
|
80
|
+
filterTruthy,
|
|
81
|
+
executeToolOperation,
|
|
82
|
+
deduplicateAndLimit,
|
|
83
|
+
deduplicate,
|
|
84
|
+
createDisposableManager,
|
|
85
|
+
applyCommonExpectations,
|
|
86
|
+
addToolErrorContext,
|
|
87
|
+
addOperationComment,
|
|
88
|
+
addNavigationComment,
|
|
89
|
+
addMouseOperationComment,
|
|
90
|
+
ToolPatterns,
|
|
91
|
+
ArrayBuilder
|
|
92
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/report-builder.ts
|
|
21
|
+
import { formatConfidence } from "./common-formatters.js";
|
|
22
|
+
|
|
23
|
+
class TextReportBuilder {
|
|
24
|
+
sections = [];
|
|
25
|
+
constructor() {
|
|
26
|
+
this.sections = [];
|
|
27
|
+
}
|
|
28
|
+
addHeader(text, level = 2) {
|
|
29
|
+
const prefix = "#".repeat(level);
|
|
30
|
+
this.sections.push(`${prefix} ${text}`);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
addLine(text) {
|
|
34
|
+
this.sections.push(text);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
addLines(lines) {
|
|
38
|
+
this.sections.push(...lines);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
addListItem(text, level = 0) {
|
|
42
|
+
const indent = " ".repeat(level);
|
|
43
|
+
this.sections.push(`${indent}- ${text}`);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
addKeyValue(key, value, level = 0) {
|
|
47
|
+
const indent = " ".repeat(level);
|
|
48
|
+
this.sections.push(`${indent}- **${key}:** ${value}`);
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
addEmptyLine() {
|
|
52
|
+
this.sections.push("");
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
addCodeBlock(code, language = "") {
|
|
56
|
+
this.sections.push(`\`\`\`${language}`);
|
|
57
|
+
this.sections.push(code);
|
|
58
|
+
this.sections.push("```");
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
addIf(condition, builder) {
|
|
62
|
+
const shouldAdd = typeof condition === "function" ? condition() : condition;
|
|
63
|
+
if (shouldAdd) {
|
|
64
|
+
builder(this);
|
|
65
|
+
}
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
addSection(title, builder, level = 2) {
|
|
69
|
+
this.addEmptyLine();
|
|
70
|
+
this.addHeader(title, level);
|
|
71
|
+
builder(this);
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
build() {
|
|
75
|
+
return this.sections.join(`
|
|
76
|
+
`);
|
|
77
|
+
}
|
|
78
|
+
getSections() {
|
|
79
|
+
return [...this.sections];
|
|
80
|
+
}
|
|
81
|
+
clear() {
|
|
82
|
+
this.sections = [];
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class PerformanceReportBuilder extends TextReportBuilder {
|
|
88
|
+
addMetric(name, value, unit, threshold, level = 0) {
|
|
89
|
+
let text = `**${name}:** ${value}${unit}`;
|
|
90
|
+
if (threshold && value > threshold) {
|
|
91
|
+
text += ` ⚠️ (threshold: ${threshold}${unit})`;
|
|
92
|
+
}
|
|
93
|
+
this.addListItem(text, level);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
addTiming(name, timeMs, thresholdMs, level = 0) {
|
|
97
|
+
return this.addMetric(name, Math.round(timeMs), "ms", thresholdMs, level);
|
|
98
|
+
}
|
|
99
|
+
addPercentage(name, value, level = 0) {
|
|
100
|
+
const percentage = Math.round(value * 100);
|
|
101
|
+
this.addListItem(`**${name}:** ${percentage}%`, level);
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class DiagnosticReportBuilder extends TextReportBuilder {
|
|
107
|
+
addDiagnosticInfo(label, value, level = 0) {
|
|
108
|
+
let formattedValue;
|
|
109
|
+
if (typeof value === "boolean") {
|
|
110
|
+
formattedValue = value ? "Yes" : "No";
|
|
111
|
+
} else if (typeof value === "number") {
|
|
112
|
+
formattedValue = value.toString();
|
|
113
|
+
} else {
|
|
114
|
+
formattedValue = value;
|
|
115
|
+
}
|
|
116
|
+
this.addKeyValue(label, formattedValue, level);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
addDiagnosticSection(title, data, formatter, level = 2) {
|
|
120
|
+
return this.addSection(title, (builder) => {
|
|
121
|
+
if (data) {
|
|
122
|
+
try {
|
|
123
|
+
formatter(data);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
builder.addListItem(`Error formatting diagnostic data: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
builder.addListItem("No data available");
|
|
129
|
+
}
|
|
130
|
+
}, level);
|
|
131
|
+
}
|
|
132
|
+
addElementCounts(counts) {
|
|
133
|
+
if (counts.total !== undefined) {
|
|
134
|
+
this.addDiagnosticInfo("Total elements", counts.total);
|
|
135
|
+
}
|
|
136
|
+
if (counts.visible !== undefined) {
|
|
137
|
+
this.addDiagnosticInfo("Visible elements", counts.visible);
|
|
138
|
+
}
|
|
139
|
+
if (counts.interactable !== undefined) {
|
|
140
|
+
this.addDiagnosticInfo("Interactable elements", counts.interactable);
|
|
141
|
+
}
|
|
142
|
+
if (counts.disabled !== undefined) {
|
|
143
|
+
this.addDiagnosticInfo("Disabled elements", counts.disabled);
|
|
144
|
+
}
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function formatFileSize(bytes) {
|
|
149
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
150
|
+
let size = bytes;
|
|
151
|
+
let unitIndex = 0;
|
|
152
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
153
|
+
size /= 1024;
|
|
154
|
+
unitIndex++;
|
|
155
|
+
}
|
|
156
|
+
return `${size.toFixed(1)}${units[unitIndex]}`;
|
|
157
|
+
}
|
|
158
|
+
function getPerformanceStatusIcon(value, thresholds) {
|
|
159
|
+
if (value <= thresholds.good) {
|
|
160
|
+
return "\uD83D\uDFE2";
|
|
161
|
+
}
|
|
162
|
+
if (value <= thresholds.warning) {
|
|
163
|
+
return "\uD83D\uDFE1";
|
|
164
|
+
}
|
|
165
|
+
return "\uD83D\uDD34";
|
|
166
|
+
}
|
|
167
|
+
function getReportStatusIcon(status) {
|
|
168
|
+
switch (status) {
|
|
169
|
+
case "success":
|
|
170
|
+
return "✅";
|
|
171
|
+
case "warning":
|
|
172
|
+
return "⚠️";
|
|
173
|
+
case "error":
|
|
174
|
+
return "\uD83D\uDEA8";
|
|
175
|
+
case "info":
|
|
176
|
+
return "ℹ️";
|
|
177
|
+
default:
|
|
178
|
+
return "⚪";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
getReportStatusIcon,
|
|
183
|
+
getPerformanceStatusIcon,
|
|
184
|
+
formatFileSize,
|
|
185
|
+
formatConfidence as formatConfidencePercentage,
|
|
186
|
+
TextReportBuilder,
|
|
187
|
+
PerformanceReportBuilder,
|
|
188
|
+
DiagnosticReportBuilder
|
|
189
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/request-logger.ts
|
|
21
|
+
import { randomBytes } from "node:crypto";
|
|
22
|
+
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { requestDebug } from "../log.js";
|
|
26
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
27
|
+
var __dirname2 = dirname(__filename2);
|
|
28
|
+
var PROJECT_ROOT = join(__dirname2, "..", "..");
|
|
29
|
+
var LOG_DIR = join(PROJECT_ROOT, "logs");
|
|
30
|
+
var LOG_FILE = join(LOG_DIR, "mcp-requests.log");
|
|
31
|
+
var FILE_LOGGING_ENABLED = process.env.PLAYWRIGHT_MCP_LOG_REQUESTS === "file" || process.env.PLAYWRIGHT_MCP_LOG_REQUESTS === "both";
|
|
32
|
+
if (FILE_LOGGING_ENABLED && !existsSync(LOG_DIR)) {
|
|
33
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
function logRequest(toolName, params) {
|
|
36
|
+
try {
|
|
37
|
+
const timestamp = new Date().toISOString();
|
|
38
|
+
const requestId = randomBytes(4).toString("hex");
|
|
39
|
+
requestDebug("Tool: %s (ID: %s) - Parameters: %o", toolName, requestId, params);
|
|
40
|
+
if (FILE_LOGGING_ENABLED) {
|
|
41
|
+
const logEntry = [
|
|
42
|
+
`[${timestamp}] Tool: ${toolName} (ID: ${requestId})`,
|
|
43
|
+
`Parameters: ${JSON.stringify(params, null, 2)}`,
|
|
44
|
+
"---"
|
|
45
|
+
].join(`
|
|
46
|
+
`);
|
|
47
|
+
appendFileSync(LOG_FILE, `${logEntry}
|
|
48
|
+
`, "utf-8");
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
requestDebug("Failed to log request: %o", error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function getLogFilePath() {
|
|
55
|
+
return LOG_FILE;
|
|
56
|
+
}
|
|
57
|
+
function logServerStart() {
|
|
58
|
+
try {
|
|
59
|
+
const timestamp = new Date().toISOString();
|
|
60
|
+
requestDebug("=== MCP Server Started === PID: %d, Node: %s", process.pid, process.version);
|
|
61
|
+
if (FILE_LOGGING_ENABLED) {
|
|
62
|
+
const logEntry = [
|
|
63
|
+
`[${timestamp}] === MCP Server Started ===`,
|
|
64
|
+
`Process ID: ${process.pid}`,
|
|
65
|
+
`Node Version: ${process.version}`,
|
|
66
|
+
`Log Mode: ${process.env.PLAYWRIGHT_MCP_LOG_REQUESTS ?? "debug only"}`,
|
|
67
|
+
"---"
|
|
68
|
+
].join(`
|
|
69
|
+
`);
|
|
70
|
+
appendFileSync(LOG_FILE, `${logEntry}
|
|
71
|
+
`, "utf-8");
|
|
72
|
+
requestDebug("Request logging to file enabled: %s", LOG_FILE);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
requestDebug("Failed to log server start: %o", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
logServerStart,
|
|
80
|
+
logRequest,
|
|
81
|
+
getLogFilePath
|
|
82
|
+
};
|