first commit
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import { createErrorResponse, ToolResult } from '@/common/tool-handler';
|
||||
import { BaseBrowserToolExecutor } from '../base-browser';
|
||||
import { TOOL_NAMES } from 'chrome-mcp-shared';
|
||||
import { TOOL_MESSAGE_TYPES } from '@/common/message-types';
|
||||
import { TIMEOUTS, ERROR_MESSAGES } from '@/common/constants';
|
||||
|
||||
interface Coordinates {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface ClickToolParams {
|
||||
selector?: string; // CSS selector for the element to click
|
||||
coordinates?: Coordinates; // Coordinates to click at (x, y relative to viewport)
|
||||
waitForNavigation?: boolean; // Whether to wait for navigation to complete after click
|
||||
timeout?: number; // Timeout in milliseconds for waiting for the element or navigation
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool for clicking elements on web pages
|
||||
*/
|
||||
class ClickTool extends BaseBrowserToolExecutor {
|
||||
name = TOOL_NAMES.BROWSER.CLICK;
|
||||
|
||||
/**
|
||||
* Execute click operation
|
||||
*/
|
||||
async execute(args: ClickToolParams): Promise<ToolResult> {
|
||||
const {
|
||||
selector,
|
||||
coordinates,
|
||||
waitForNavigation = false,
|
||||
timeout = TIMEOUTS.DEFAULT_WAIT * 5,
|
||||
} = args;
|
||||
|
||||
console.log(`Starting click operation with options:`, args);
|
||||
|
||||
if (!selector && !coordinates) {
|
||||
return createErrorResponse(
|
||||
ERROR_MESSAGES.INVALID_PARAMETERS + ': Either selector or coordinates must be provided',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Get current tab
|
||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tabs[0]) {
|
||||
return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND);
|
||||
}
|
||||
|
||||
const tab = tabs[0];
|
||||
if (!tab.id) {
|
||||
return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND + ': Active tab has no ID');
|
||||
}
|
||||
|
||||
await this.injectContentScript(tab.id, ['inject-scripts/click-helper.js']);
|
||||
|
||||
// Send click message to content script
|
||||
const result = await this.sendMessageToTab(tab.id, {
|
||||
action: TOOL_MESSAGE_TYPES.CLICK_ELEMENT,
|
||||
selector,
|
||||
coordinates,
|
||||
waitForNavigation,
|
||||
timeout,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: result.message || 'Click operation successful',
|
||||
elementInfo: result.elementInfo,
|
||||
navigationOccurred: result.navigationOccurred,
|
||||
clickMethod: coordinates ? 'coordinates' : 'selector',
|
||||
}),
|
||||
},
|
||||
],
|
||||
isError: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in click operation:', error);
|
||||
return createErrorResponse(
|
||||
`Error performing click: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const clickTool = new ClickTool();
|
||||
|
||||
interface FillToolParams {
|
||||
selector: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool for filling form elements on web pages
|
||||
*/
|
||||
class FillTool extends BaseBrowserToolExecutor {
|
||||
name = TOOL_NAMES.BROWSER.FILL;
|
||||
|
||||
/**
|
||||
* Execute fill operation
|
||||
*/
|
||||
async execute(args: FillToolParams): Promise<ToolResult> {
|
||||
const { selector, value } = args;
|
||||
|
||||
console.log(`Starting fill operation with options:`, args);
|
||||
|
||||
if (!selector) {
|
||||
return createErrorResponse(ERROR_MESSAGES.INVALID_PARAMETERS + ': Selector must be provided');
|
||||
}
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
return createErrorResponse(ERROR_MESSAGES.INVALID_PARAMETERS + ': Value must be provided');
|
||||
}
|
||||
|
||||
try {
|
||||
// Get current tab
|
||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tabs[0]) {
|
||||
return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND);
|
||||
}
|
||||
|
||||
const tab = tabs[0];
|
||||
if (!tab.id) {
|
||||
return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND + ': Active tab has no ID');
|
||||
}
|
||||
|
||||
await this.injectContentScript(tab.id, ['inject-scripts/fill-helper.js']);
|
||||
|
||||
// Send fill message to content script
|
||||
const result = await this.sendMessageToTab(tab.id, {
|
||||
action: TOOL_MESSAGE_TYPES.FILL_ELEMENT,
|
||||
selector,
|
||||
value,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return createErrorResponse(result.error);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: result.message || 'Fill operation successful',
|
||||
elementInfo: result.elementInfo,
|
||||
}),
|
||||
},
|
||||
],
|
||||
isError: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in fill operation:', error);
|
||||
return createErrorResponse(
|
||||
`Error filling element: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const fillTool = new FillTool();
|
Reference in New Issue
Block a user