@calmhive/calmhive-cli
Version:
🐝 Intelligent Claude CLI wrapper with rule injection, AFk background processing, voice interface, and adaptive retry
593 lines (528 loc) • 14.5 kB
JavaScript
/**
* Standardized Error Handling Framework
*
* Provides consistent error handling across all Calmhive modules with:
* - Structured error types with categorization
* - Error context and stack trace management
* - Recovery strategy integration
* - Logging and monitoring integration
* - Circuit breaker compatibility
*/
/**
* Base error class for all Calmhive errors
*/
class CalmhiveError extends Error {
constructor(message, options = {}) {
super(message);
this.name = this.constructor.name;
// Error metadata
this.category = options.category || 'UNKNOWN';
this.code = options.code || 'UNKNOWN_ERROR';
this.severity = options.severity || 'medium';
this.context = options.context || {};
this.timestamp = Date.now();
this.retryable = options.retryable !== false; // Default to retryable
this.userMessage = options.userMessage || null;
// Capture stack trace
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
// Chain original error if provided
if (options.cause) {
this.cause = options.cause;
}
}
/**
* Convert error to structured object for logging/serialization
*/
toObject() {
return {
name: this.name,
message: this.message,
category: this.category,
code: this.code,
severity: this.severity,
context: this.context,
timestamp: this.timestamp,
retryable: this.retryable,
userMessage: this.userMessage,
stack: this.stack,
cause: this.cause ? (this.cause.toObject ? this.cause.toObject() : this.cause.message) : null
};
}
/**
* Get user-friendly message
*/
getUserMessage() {
return this.userMessage || this.message;
}
/**
* Check if error is retryable
*/
isRetryable() {
return this.retryable;
}
/**
* Get error severity
*/
getSeverity() {
return this.severity;
}
}
/**
* Session-related errors
*/
class SessionError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'SESSION',
...options
});
}
}
class SessionNotFoundError extends SessionError {
constructor(sessionId, options = {}) {
super(`Session not found: ${sessionId}`, {
code: 'SESSION_NOT_FOUND',
context: { sessionId },
userMessage: 'The requested session could not be found.',
retryable: false,
...options
});
}
}
class SessionStateError extends SessionError {
constructor(sessionId, currentState, expectedState, options = {}) {
super(`Invalid session state: ${currentState}, expected: ${expectedState}`, {
code: 'SESSION_INVALID_STATE',
context: { sessionId, currentState, expectedState },
userMessage: 'Session is in an unexpected state. Please try refreshing.',
...options
});
}
}
class SessionTimeoutError extends SessionError {
constructor(sessionId, timeout, options = {}) {
super(`Session timed out after ${timeout}ms`, {
code: 'SESSION_TIMEOUT',
severity: 'high',
context: { sessionId, timeout },
userMessage: 'Session took too long to complete and was terminated.',
retryable: true,
...options
});
}
}
/**
* Process-related errors
*/
class ProcessError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'PROCESS',
...options
});
}
}
class ProcessSpawnError extends ProcessError {
constructor(command, error, options = {}) {
super(`Failed to spawn process: ${command}`, {
code: 'PROCESS_SPAWN_FAILED',
severity: 'high',
context: { command, originalError: error.message },
userMessage: 'Failed to start the required process. Please check system resources.',
cause: error,
...options
});
}
}
class ProcessKillError extends ProcessError {
constructor(pid, signal, options = {}) {
super(`Failed to kill process ${pid} with signal ${signal}`, {
code: 'PROCESS_KILL_FAILED',
context: { pid, signal },
userMessage: 'Unable to stop the process. You may need to manually terminate it.',
...options
});
}
}
/**
* Database-related errors
*/
class DatabaseError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'DATABASE',
...options
});
}
}
class DatabaseConnectionError extends DatabaseError {
constructor(error, options = {}) {
super('Database connection failed', {
code: 'DB_CONNECTION_FAILED',
severity: 'critical',
context: { originalError: error.message },
userMessage: 'Unable to connect to the database. Please check the system status.',
cause: error,
retryable: true,
...options
});
}
}
class DatabaseTransactionError extends DatabaseError {
constructor(operation, error, options = {}) {
super(`Database transaction failed: ${operation}`, {
code: 'DB_TRANSACTION_FAILED',
severity: 'high',
context: { operation, originalError: error.message },
userMessage: 'Database operation failed. Your changes may not have been saved.',
cause: error,
retryable: true,
...options
});
}
}
class DatabaseLockError extends DatabaseError {
constructor(table, timeout, options = {}) {
super(`Database lock timeout on ${table} after ${timeout}ms`, {
code: 'DB_LOCK_TIMEOUT',
context: { table, timeout },
userMessage: 'Database is busy. Please try again in a moment.',
retryable: true,
...options
});
}
}
/**
* Network/API-related errors
*/
class NetworkError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'NETWORK',
...options
});
}
}
class ClaudeAPIError extends NetworkError {
constructor(status, message, options = {}) {
super(`Claude API error (${status}): ${message}`, {
code: 'CLAUDE_API_ERROR',
severity: status >= 500 ? 'high' : 'medium',
context: { status, apiMessage: message },
userMessage: 'Claude API is experiencing issues. Please try again later.',
retryable: status >= 500 || status === 429, // Retry server errors and rate limits
...options
});
}
}
class NetworkTimeoutError extends NetworkError {
constructor(url, timeout, options = {}) {
super(`Network request timed out: ${url}`, {
code: 'NETWORK_TIMEOUT',
context: { url, timeout },
userMessage: 'Network request timed out. Please check your connection.',
retryable: true,
...options
});
}
}
/**
* File system errors
*/
class FileSystemError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'FILESYSTEM',
...options
});
}
}
class FileNotFoundError extends FileSystemError {
constructor(filePath, options = {}) {
super(`File not found: ${filePath}`, {
code: 'FILE_NOT_FOUND',
context: { filePath },
userMessage: 'Required file could not be found.',
retryable: false,
...options
});
}
}
class FilePermissionError extends FileSystemError {
constructor(filePath, operation, options = {}) {
super(`Permission denied for ${operation} on ${filePath}`, {
code: 'FILE_PERMISSION_DENIED',
context: { filePath, operation },
userMessage: 'Insufficient permissions to access the file.',
retryable: false,
...options
});
}
}
class DiskSpaceError extends FileSystemError {
constructor(path, requiredSpace, availableSpace, options = {}) {
super(`Insufficient disk space: need ${requiredSpace}MB, have ${availableSpace}MB`, {
code: 'DISK_SPACE_INSUFFICIENT',
severity: 'high',
context: { path, requiredSpace, availableSpace },
userMessage: 'Not enough disk space available. Please free up some space.',
retryable: false,
...options
});
}
}
/**
* Configuration errors
*/
class ConfigurationError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'CONFIGURATION',
...options
});
}
}
class ConfigValidationError extends ConfigurationError {
constructor(field, value, expected, options = {}) {
super(`Invalid configuration: ${field} = ${value}, expected ${expected}`, {
code: 'CONFIG_VALIDATION_FAILED',
context: { field, value, expected },
userMessage: 'Configuration is invalid. Please check your settings.',
retryable: false,
...options
});
}
}
class ConfigMissingError extends ConfigurationError {
constructor(field, options = {}) {
super(`Missing required configuration: ${field}`, {
code: 'CONFIG_MISSING',
context: { field },
userMessage: 'Required configuration is missing. Please check your setup.',
retryable: false,
...options
});
}
}
/**
* User input errors
*/
class UserError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'USER_INPUT',
severity: 'low',
retryable: false,
...options
});
}
}
class ValidationError extends UserError {
constructor(field, value, constraint, options = {}) {
super(`Validation failed for ${field}: ${constraint}`, {
code: 'VALIDATION_FAILED',
context: { field, value, constraint },
userMessage: `Invalid ${field}: ${constraint}`,
...options
});
}
}
/**
* System errors
*/
class SystemError extends CalmhiveError {
constructor(message, options = {}) {
super(message, {
category: 'SYSTEM',
severity: 'high',
...options
});
}
}
class MemoryError extends SystemError {
constructor(operation, memoryUsed, memoryLimit, options = {}) {
super(`Memory limit exceeded during ${operation}`, {
code: 'MEMORY_LIMIT_EXCEEDED',
severity: 'critical',
context: { operation, memoryUsed, memoryLimit },
userMessage: 'System is low on memory. Please close other applications.',
retryable: true,
...options
});
}
}
/**
* Error handling utilities
*/
class ErrorHandler {
constructor(options = {}) {
this.logger = options.logger || console;
this.monitor = options.monitor || null;
this.circuitBreaker = options.circuitBreaker || null;
}
/**
* Handle error with appropriate logging and recovery
*/
handle(error, context = {}) {
const calmhiveError = this.normalizeError(error, context);
// Log error
this.logError(calmhiveError);
// Report to monitoring
if (this.monitor) {
this.monitor.recordError(calmhiveError);
}
// Update circuit breaker
if (this.circuitBreaker && calmhiveError.category === 'NETWORK') {
this.circuitBreaker.recordFailure();
}
return calmhiveError;
}
/**
* Normalize any error to CalmhiveError
*/
normalizeError(error, context = {}) {
if (error instanceof CalmhiveError) {
// Add additional context if provided
if (Object.keys(context).length > 0) {
error.context = { ...error.context, ...context };
}
return error;
}
// Convert regular Error to CalmhiveError
return new CalmhiveError(error.message || 'Unknown error', {
cause: error,
context,
code: error.code || 'UNKNOWN_ERROR'
});
}
/**
* Log error with appropriate level
*/
logError(error) {
const logData = {
timestamp: new Date().toISOString(),
...error.toObject()
};
switch (error.severity) {
case 'critical':
this.logger.error('CRITICAL ERROR:', logData);
break;
case 'high':
this.logger.error('ERROR:', logData);
break;
case 'medium':
this.logger.warn('WARNING:', logData);
break;
case 'low':
this.logger.info('INFO:', logData);
break;
default:
this.logger.error('ERROR:', logData);
}
}
/**
* Execute function with error handling
*/
async withErrorHandling(fn, context = {}) {
try {
return await fn();
} catch (error) {
throw this.handle(error, context);
}
}
/**
* Create error recovery wrapper
*/
withRecovery(fn, recoveryFn, maxRetries = 3) {
return async (...args) => {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn(...args);
} catch (error) {
lastError = this.handle(error, { attempt, maxRetries });
if (!lastError.isRetryable() || attempt === maxRetries) {
throw lastError;
}
// Apply recovery strategy
if (recoveryFn) {
await recoveryFn(lastError, attempt);
}
// Exponential backoff
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
};
}
}
/**
* Create pre-configured error handler for different contexts
*/
function createErrorHandler(context) {
const handlers = {
session: new ErrorHandler({ context: 'session' }),
process: new ErrorHandler({ context: 'process' }),
database: new ErrorHandler({ context: 'database' }),
network: new ErrorHandler({ context: 'network' }),
filesystem: new ErrorHandler({ context: 'filesystem' })
};
return handlers[context] || new ErrorHandler();
}
/**
* Utility function to wrap async functions with error handling
*/
function withErrorHandler(fn, context = {}) {
const handler = new ErrorHandler();
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
throw handler.handle(error, context);
}
};
}
module.exports = {
// Base classes
CalmhiveError,
ErrorHandler,
// Session errors
SessionError,
SessionNotFoundError,
SessionStateError,
SessionTimeoutError,
// Process errors
ProcessError,
ProcessSpawnError,
ProcessKillError,
// Database errors
DatabaseError,
DatabaseConnectionError,
DatabaseTransactionError,
DatabaseLockError,
// Network errors
NetworkError,
ClaudeAPIError,
NetworkTimeoutError,
// File system errors
FileSystemError,
FileNotFoundError,
FilePermissionError,
DiskSpaceError,
// Configuration errors
ConfigurationError,
ConfigValidationError,
ConfigMissingError,
// User errors
UserError,
ValidationError,
// System errors
SystemError,
MemoryError,
// Utilities
createErrorHandler,
withErrorHandler
};