Files
broswer-automation/app/chrome-extension/entrypoints/background/tools/browser/enhanced-search.ts

185 lines
5.3 KiB
TypeScript

import { BaseBrowserToolExecutor } from '../base-browser';
import { createErrorResponse, createSuccessResponse } from '../../../../common/tool-handler';
import { ERROR_MESSAGES } from '../../../../common/constants';
export class EnhancedSearchTool extends BaseBrowserToolExecutor {
async chromeSearchGoogle(args: {
query: string;
openGoogle?: boolean;
extractResults?: boolean;
maxResults?: number;
}) {
const { query, openGoogle = true, extractResults = true, maxResults = 10 } = args;
try {
// Step 1: Navigate to Google if requested
if (openGoogle) {
await this.navigateToGoogle();
await this.sleep(3000); // Wait for page to load
}
// Step 2: Find and fill search box
const searchSuccess = await this.performGoogleSearch(query);
if (!searchSuccess) {
return createErrorResponse(
'Failed to perform Google search - could not find or interact with search box',
);
}
// Step 3: Wait for results to load
await this.sleep(3000);
// Step 4: Extract results if requested
if (extractResults) {
const results = await this.extractSearchResults(maxResults);
return createSuccessResponse({
query,
searchCompleted: true,
resultsExtracted: true,
results,
});
}
return createSuccessResponse({
query,
searchCompleted: true,
resultsExtracted: false,
message: 'Google search completed successfully',
});
} catch (error) {
return createErrorResponse(
`Error performing Google search: ${error instanceof Error ? error.message : 'Unknown error'}`,
);
}
}
async chromeSubmitForm(args: {
formSelector?: string;
inputSelector?: string;
submitMethod?: 'enter' | 'button' | 'auto';
}) {
const { formSelector = 'form', inputSelector, submitMethod = 'auto' } = args;
try {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tabs[0]?.id) {
return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND);
}
const tabId = tabs[0].id;
// Inject form submission script
await this.injectContentScript(tabId, ['inject-scripts/form-submit-helper.js']);
const result = await this.sendMessageToTab(tabId, {
action: 'submitForm',
formSelector,
inputSelector,
submitMethod,
});
if (result.error) {
return createErrorResponse(result.error);
}
return createSuccessResponse(result);
} catch (error) {
return createErrorResponse(
`Error submitting form: ${error instanceof Error ? error.message : 'Unknown error'}`,
);
}
}
private async navigateToGoogle(): Promise<void> {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tabs[0]?.id) {
throw new Error('No active tab found');
}
await chrome.tabs.update(tabs[0].id, { url: 'https://www.google.com' });
}
private async performGoogleSearch(query: string): Promise<boolean> {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tabs[0]?.id) {
throw new Error('No active tab found');
}
const tabId = tabs[0].id;
// Enhanced search box selectors
const searchSelectors = [
'#APjFqb', // Main Google search box ID
'textarea[name="q"]', // Google search textarea
'input[name="q"]', // Google search input (fallback)
'[role="combobox"]', // Role-based selector
'.gLFyf', // Google search box class
'textarea[aria-label*="Search"]', // Aria-label based
'[title*="Search"]', // Title attribute
'.gsfi', // Google search field input class
'#lst-ib', // Alternative Google search ID
'input[type="search"]', // Generic search input
'textarea[role="combobox"]', // Textarea with combobox role
];
// Inject search helper script
await this.injectContentScript(tabId, ['inject-scripts/enhanced-search-helper.js']);
for (const selector of searchSelectors) {
try {
const result = await this.sendMessageToTab(tabId, {
action: 'performGoogleSearch',
selector,
query,
});
if (result.success) {
return true;
}
} catch (error) {
console.debug(`Search selector ${selector} failed:`, error);
continue;
}
}
return false;
}
private async extractSearchResults(maxResults: number): Promise<any[]> {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tabs[0]?.id) {
throw new Error('No active tab found');
}
const tabId = tabs[0].id;
const result = await this.sendMessageToTab(tabId, {
action: 'extractSearchResults',
maxResults,
});
return result.results || [];
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
// Export tool instances
export const searchGoogleTool = new (class extends EnhancedSearchTool {
name = 'chrome_search_google';
async execute(args: any) {
return await this.chromeSearchGoogle(args);
}
})();
export const submitFormTool = new (class extends EnhancedSearchTool {
name = 'chrome_submit_form';
async execute(args: any) {
return await this.chromeSubmitForm(args);
}
})();