UNPKG

@puberty-labs/clits

Version:

CLiTS (Chrome Logging and Inspection Tool Suite) is a powerful Node.js library for AI-controlled Chrome browser automation, testing, and inspection. Features enhanced CSS selector support (:contains(), XPath), dry-run mode, element discovery tools, and co

201 lines (200 loc) 8.16 kB
// BSD: AI-friendly error handler utility for CLITS. Provides structured error responses for AI assistants with recovery suggestions. export class AIErrorHandler { constructor() { this.jsonErrorsEnabled = false; } static getInstance() { if (!AIErrorHandler.instance) { AIErrorHandler.instance = new AIErrorHandler(); } return AIErrorHandler.instance; } enableJsonErrors() { this.jsonErrorsEnabled = true; } disableJsonErrors() { this.jsonErrorsEnabled = false; } handleError(error, context, traditionalMessage) { const errorMessage = error instanceof Error ? error.message : error; const errorCode = this.categorizeError(errorMessage); if (this.jsonErrorsEnabled) { const aiError = this.createAIErrorResponse(errorMessage, errorCode, context); console.log(JSON.stringify(aiError, null, 2)); } else { // Traditional console error output const prefix = this.getCommandPrefix(context.command); console.error(`${prefix} ${traditionalMessage || errorMessage}`); } } handleSuccess(data) { if (this.jsonErrorsEnabled) { const aiSuccess = { success: true, data, metadata: { version: process.env.npm_package_version || '1.2.0', platform: process.platform, timestamp: new Date().toISOString(), } }; console.log(JSON.stringify(aiSuccess, null, 2)); } else { // Traditional success output (if any) if (data && typeof data === 'object') { console.log(JSON.stringify(data, null, 2)); } } } categorizeError(message) { const lowerMessage = message.toLowerCase(); // ELEMENT_NOT_FOUND if (lowerMessage.includes('element') && (lowerMessage.includes('not found') || lowerMessage.includes('could not locate'))) { return { code: 'ELEMENT_NOT_FOUND', category: 'AUTOMATION', severity: 'ERROR', recovery_suggestions: [ 'Wait for page to fully load and retry', 'Check if element exists with \'inspect --find-clickable\'', 'Take screenshot with \'vision --screenshot\' to diagnose', 'Try alternative selectors: \'[aria-label="Save"]\' or text-based targeting' ], requires_human_intervention: false }; } // AUTH_REQUIRED if (lowerMessage.includes('login') || lowerMessage.includes('authentication') || lowerMessage.includes('unauthorized')) { return { code: 'AUTH_REQUIRED', category: 'AUTH', severity: 'ERROR', recovery_suggestions: [ 'Request user to log in manually', 'Wait for user confirmation before proceeding', 'Check if already logged in on different tab' ], requires_human_intervention: true }; } // CHROME_NOT_CONNECTED if (lowerMessage.includes('chrome') && (lowerMessage.includes('connect') || lowerMessage.includes('debugging') || lowerMessage.includes('port'))) { return { code: 'CHROME_NOT_CONNECTED', category: 'SYSTEM', severity: 'ERROR', recovery_suggestions: [ 'Verify Chrome is running with --remote-debugging-port=9222', 'Check if port 9222 is available and not blocked', 'Restart Chrome with debugging enabled', 'Try alternative port if 9222 is occupied' ], requires_human_intervention: false }; } // TIMEOUT_EXCEEDED if (lowerMessage.includes('timeout') || lowerMessage.includes('timed out')) { return { code: 'TIMEOUT_EXCEEDED', category: 'AUTOMATION', severity: 'WARNING', recovery_suggestions: [ 'Take screenshot to check current page state', 'Increase timeout duration for slow operations', 'Check if element selector is correct', 'Proceed with caution - page may still be loading' ], requires_human_intervention: false }; } // PAGE_UNRESPONSIVE if (lowerMessage.includes('unresponsive') || lowerMessage.includes('failed to respond')) { return { code: 'PAGE_UNRESPONSIVE', category: 'NETWORK', severity: 'WARNING', recovery_suggestions: [ 'Increase timeout and retry navigation', 'Check network connectivity', 'Try refreshing the page', 'Verify the URL is correct and accessible' ], requires_human_intervention: false }; } // INVALID_SELECTOR if (lowerMessage.includes('selector') && (lowerMessage.includes('invalid') || lowerMessage.includes('malformed') || lowerMessage.includes('syntax'))) { return { code: 'INVALID_SELECTOR', category: 'AUTOMATION', severity: 'ERROR', recovery_suggestions: [ 'Fix CSS selector syntax', 'Use browser dev tools to test selector', 'Try alternative targeting methods', 'Use text-based targeting as fallback' ], requires_human_intervention: false }; } // PERMISSION_DENIED if (lowerMessage.includes('permission') || lowerMessage.includes('security') || lowerMessage.includes('blocked')) { return { code: 'PERMISSION_DENIED', category: 'SYSTEM', severity: 'ERROR', recovery_suggestions: [ 'Request user to perform action manually', 'Use keyboard navigation as alternative', 'Check if element can be targeted differently' ], requires_human_intervention: true }; } // Generic error return { code: 'UNKNOWN_ERROR', category: 'SYSTEM', severity: 'ERROR', recovery_suggestions: [ 'Check Chrome debugging connection', 'Verify page is fully loaded', 'Take screenshot to diagnose current state' ], requires_human_intervention: false }; } createAIErrorResponse(message, errorInfo, context) { return { success: false, error: { code: errorInfo.code, message, category: errorInfo.category, severity: errorInfo.severity, context: { ...context, timestamp: new Date().toISOString() }, recovery_suggestions: errorInfo.recovery_suggestions, requires_human_intervention: errorInfo.requires_human_intervention } }; } getCommandPrefix(command) { const prefixes = { 'extract': '[CLiTS-INSPECTOR]', 'navigate': '[CLiTS-NAVIGATOR]', 'interact': '[CLiTS-INTERACTOR]', 'automate': '[CLiTS-AUTOMATOR]', 'inspect': '[CLiTS-INSPECTOR]', 'discover-links': '[CLiTS-DISCOVER-LINKS]', 'discover-tabs': '[CLiTS-DISCOVER-TABS]', 'chrome-control': '[CLiTS-CHROME-CONTROL]', 'vision': '[CLiTS-VISION]' }; return prefixes[command] || '[CLiTS]'; } }