@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
JavaScript
// 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]';
}
}