chrometools-mcp 2.4.2 → 3.1.2
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/CHANGELOG.md +540 -0
- package/COMPONENT_MAPPING_SPEC.md +1217 -0
- package/README.md +494 -38
- package/bridge/bridge-client.js +472 -0
- package/bridge/bridge-service.js +399 -0
- package/bridge/install.js +241 -0
- package/browser/browser-manager.js +107 -2
- package/browser/page-manager.js +226 -69
- package/docs/CHROME_EXTENSION.md +219 -0
- package/docs/PAGE_OBJECT_MODEL_CONCEPT.md +1756 -0
- package/element-finder-utils.js +138 -28
- package/extension/background.js +643 -0
- package/extension/content.js +715 -0
- package/extension/icons/create-icons.js +164 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +58 -0
- package/extension/popup/popup.css +437 -0
- package/extension/popup/popup.html +102 -0
- package/extension/popup/popup.js +415 -0
- package/extension/recorder-overlay.css +93 -0
- package/figma-tools.js +120 -0
- package/index.js +3347 -2518
- package/models/BaseInputModel.js +93 -0
- package/models/CheckboxGroupModel.js +199 -0
- package/models/CheckboxModel.js +103 -0
- package/models/ColorInputModel.js +53 -0
- package/models/DateInputModel.js +67 -0
- package/models/RadioGroupModel.js +126 -0
- package/models/RangeInputModel.js +60 -0
- package/models/SelectModel.js +97 -0
- package/models/TextInputModel.js +34 -0
- package/models/TextareaModel.js +59 -0
- package/models/TimeInputModel.js +49 -0
- package/models/index.js +122 -0
- package/package.json +3 -2
- package/pom/apom-converter.js +267 -0
- package/pom/apom-tree-converter.js +515 -0
- package/pom/element-id-generator.js +175 -0
- package/recorder/page-object-generator.js +16 -0
- package/recorder/scenario-executor.js +80 -2
- package/server/tool-definitions.js +839 -656
- package/server/tool-groups.js +3 -2
- package/server/tool-schemas.js +367 -296
- package/server/websocket-bridge.js +447 -0
- package/utils/selector-resolver.js +186 -0
- package/utils/ui-framework-detector.js +392 -0
|
@@ -159,10 +159,22 @@ async function executeSingleScenario(scenario, page, params = {}, options = {})
|
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
try {
|
|
162
|
-
for (
|
|
162
|
+
for (let i = 0; i < scenario.chain.length; i++) {
|
|
163
|
+
const action = scenario.chain[i];
|
|
164
|
+
|
|
163
165
|
// Substitute parameters in action
|
|
164
166
|
const resolvedAction = substituteParameters(action, params);
|
|
165
167
|
|
|
168
|
+
// Special handling for openTab with empty URL: look ahead to next action's URL
|
|
169
|
+
if (resolvedAction.type === 'openTab' && (!resolvedAction.data.url || resolvedAction.data.url === '')) {
|
|
170
|
+
const nextAction = scenario.chain[i + 1];
|
|
171
|
+
if (nextAction && nextAction.tabUrl && nextAction.tabUrl !== '') {
|
|
172
|
+
// Next action has a real URL, use it for openTab
|
|
173
|
+
debugLog(`[Executor] openTab has empty URL, using next action's URL: ${nextAction.tabUrl}`);
|
|
174
|
+
resolvedAction.data.url = nextAction.tabUrl;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
166
178
|
// Execute action with retry
|
|
167
179
|
const actionResult = await executeActionWithRetry(
|
|
168
180
|
resolvedAction,
|
|
@@ -178,6 +190,11 @@ async function executeSingleScenario(scenario, page, params = {}, options = {})
|
|
|
178
190
|
return result;
|
|
179
191
|
}
|
|
180
192
|
|
|
193
|
+
// Update page if action returned a new page (e.g., openTab switched tabs)
|
|
194
|
+
if (actionResult.page) {
|
|
195
|
+
page = actionResult.page;
|
|
196
|
+
}
|
|
197
|
+
|
|
181
198
|
// Store outputs if action produces any
|
|
182
199
|
if (actionResult.output) {
|
|
183
200
|
Object.assign(result.outputs, actionResult.output);
|
|
@@ -270,6 +287,7 @@ async function executeActionWithRetry(action, page, maxRetries, timeout) {
|
|
|
270
287
|
|
|
271
288
|
result.success = true;
|
|
272
289
|
result.output = actionResult.output;
|
|
290
|
+
result.page = actionResult.page; // Pass through the updated page
|
|
273
291
|
attemptInfo.success = true;
|
|
274
292
|
result.errorDetails.attempts.push(attemptInfo);
|
|
275
293
|
return result;
|
|
@@ -491,7 +509,7 @@ function formatDetailedError(action, errorDetails) {
|
|
|
491
509
|
* Execute single action
|
|
492
510
|
*/
|
|
493
511
|
async function executeAction(action, page, timeout) {
|
|
494
|
-
const result = { output: null };
|
|
512
|
+
const result = { output: null, page: page }; // Return page in result
|
|
495
513
|
|
|
496
514
|
switch (action.type) {
|
|
497
515
|
case 'click':
|
|
@@ -538,6 +556,14 @@ async function executeAction(action, page, timeout) {
|
|
|
538
556
|
result.output = await executeExtract(action, page);
|
|
539
557
|
break;
|
|
540
558
|
|
|
559
|
+
case 'openTab':
|
|
560
|
+
// openTab may return a new page to switch to
|
|
561
|
+
const newPage = await executeOpenTab(action, page, timeout);
|
|
562
|
+
if (newPage) {
|
|
563
|
+
result.page = newPage;
|
|
564
|
+
}
|
|
565
|
+
break;
|
|
566
|
+
|
|
541
567
|
default:
|
|
542
568
|
throw new Error(`Unknown action type: ${action.type}`);
|
|
543
569
|
}
|
|
@@ -948,6 +974,58 @@ function substituteParameters(action, params) {
|
|
|
948
974
|
return resolved;
|
|
949
975
|
}
|
|
950
976
|
|
|
977
|
+
/**
|
|
978
|
+
* Open a tab with URL and optionally switch to it
|
|
979
|
+
* This ensures the tab exists during playback
|
|
980
|
+
*/
|
|
981
|
+
async function executeOpenTab(action, page, timeout) {
|
|
982
|
+
const { url, switchToTab } = action.data;
|
|
983
|
+
|
|
984
|
+
// Skip openTab actions with empty URL or about:blank (after look-ahead failed to find URL)
|
|
985
|
+
if (!url || url === '' || url === 'about:blank') {
|
|
986
|
+
debugLog(`[openTab] Skipping empty/blank tab switch - keeping current page`);
|
|
987
|
+
return null; // Don't switch, keep current page
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Wait for previous tab's processes to complete before switching
|
|
991
|
+
// This prevents issues with pending navigations, AJAX calls, or form submissions
|
|
992
|
+
debugLog(`[openTab] Waiting 500ms for previous tab to settle...`);
|
|
993
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
994
|
+
|
|
995
|
+
debugLog(`[openTab] Opening tab: ${url}, switchToTab: ${switchToTab}`);
|
|
996
|
+
|
|
997
|
+
// Get browser instance
|
|
998
|
+
const browser = page.browser();
|
|
999
|
+
|
|
1000
|
+
// Check if tab with this URL already exists
|
|
1001
|
+
const pages = await browser.pages();
|
|
1002
|
+
let targetPage = pages.find(p => p.url() === url);
|
|
1003
|
+
|
|
1004
|
+
if (targetPage) {
|
|
1005
|
+
// Tab already exists, just switch to it
|
|
1006
|
+
debugLog(`[openTab] Tab with URL ${url} already exists, switching to it`);
|
|
1007
|
+
} else {
|
|
1008
|
+
// Create new tab
|
|
1009
|
+
targetPage = await browser.newPage();
|
|
1010
|
+
|
|
1011
|
+
await targetPage.goto(url, {
|
|
1012
|
+
waitUntil: 'domcontentloaded', // Less strict for sites with continuous loading
|
|
1013
|
+
timeout
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
debugLog(`[openTab] New tab created: ${url}`);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// If switchToTab is true, bring the tab to front and return the new page
|
|
1020
|
+
if (switchToTab && targetPage) {
|
|
1021
|
+
await targetPage.bringToFront();
|
|
1022
|
+
debugLog(`[openTab] Switched to tab: ${url}`);
|
|
1023
|
+
return targetPage; // Return the new page for subsequent actions
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
return null; // Don't switch page if switchToTab is false
|
|
1027
|
+
}
|
|
1028
|
+
|
|
951
1029
|
/**
|
|
952
1030
|
* Substitute {{param}} in string
|
|
953
1031
|
*/
|