mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 08:17:22 +00:00
- add errors/ directory with error types, handler, cleanup registry - extract claude-spawner.ts from auth modules - update imports across auth, ccs, web-server Phase 2 & 4: Error handling consolidation
168 lines
4.0 KiB
TypeScript
168 lines
4.0 KiB
TypeScript
/**
|
|
* Centralized error handler for CCS CLI
|
|
*
|
|
* Provides unified error handling with:
|
|
* - Consistent error formatting
|
|
* - Exit code management
|
|
* - Cleanup callback execution
|
|
* - Debug mode support
|
|
*/
|
|
|
|
import { ExitCode, EXIT_CODE_DESCRIPTIONS } from './exit-codes';
|
|
import { isCCSError } from './error-types';
|
|
import { runCleanup } from './cleanup-registry';
|
|
|
|
/**
|
|
* Debug mode flag - set via CCS_DEBUG environment variable
|
|
*/
|
|
const isDebugMode = (): boolean => {
|
|
return process.env['CCS_DEBUG'] === '1' || process.env['CCS_DEBUG'] === 'true';
|
|
};
|
|
|
|
/**
|
|
* Format error message for display
|
|
* Uses ASCII-only formatting per codebase standards (no emojis)
|
|
*/
|
|
function formatErrorMessage(error: unknown): string {
|
|
if (isCCSError(error)) {
|
|
return `[X] ${error.message}`;
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
return `[X] ${error.message}`;
|
|
}
|
|
|
|
if (typeof error === 'string') {
|
|
return `[X] ${error}`;
|
|
}
|
|
|
|
return '[X] An unexpected error occurred';
|
|
}
|
|
|
|
/**
|
|
* Get exit code from error
|
|
* CCSError types have their own codes, others default to GENERAL_ERROR
|
|
*/
|
|
function getExitCode(error: unknown): ExitCode {
|
|
if (isCCSError(error)) {
|
|
return error.code;
|
|
}
|
|
return ExitCode.GENERAL_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Log debug information for an error
|
|
* Only outputs when CCS_DEBUG is enabled
|
|
*/
|
|
function logDebugInfo(error: unknown, code: ExitCode): void {
|
|
if (!isDebugMode()) return;
|
|
|
|
console.error('');
|
|
console.error('[i] Debug information:');
|
|
console.error(` Exit code: ${code} (${EXIT_CODE_DESCRIPTIONS[code] || 'Unknown'})`);
|
|
|
|
if (error instanceof Error) {
|
|
console.error(` Error type: ${error.constructor.name}`);
|
|
if (error.stack) {
|
|
console.error(' Stack trace:');
|
|
const stackLines = error.stack.split('\n').slice(1, 6);
|
|
for (const line of stackLines) {
|
|
console.error(` ${line.trim()}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Log additional properties for CCSError types
|
|
if (isCCSError(error)) {
|
|
console.error(` Recoverable: ${error.recoverable}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Central error handler
|
|
* Formats error, runs cleanup, and exits with appropriate code
|
|
*
|
|
* @param error - The error to handle
|
|
* @returns never - Always exits the process
|
|
*/
|
|
export function handleError(error: unknown): never {
|
|
// Run cleanup callbacks first
|
|
runCleanup();
|
|
|
|
const code = getExitCode(error);
|
|
const message = formatErrorMessage(error);
|
|
|
|
// Output error message to stderr
|
|
console.error(message);
|
|
|
|
// Log debug info if enabled
|
|
logDebugInfo(error, code);
|
|
|
|
// Exit with appropriate code
|
|
process.exit(code);
|
|
}
|
|
|
|
/**
|
|
* Exit with an error message and code
|
|
* Convenience function for simple error exits
|
|
*
|
|
* @param message - Error message to display
|
|
* @param code - Exit code (defaults to GENERAL_ERROR)
|
|
* @returns never - Always exits the process
|
|
*/
|
|
export function exitWithError(message: string, code: ExitCode = ExitCode.GENERAL_ERROR): never {
|
|
runCleanup();
|
|
console.error(`[X] ${message}`);
|
|
|
|
if (isDebugMode()) {
|
|
console.error('');
|
|
console.error(`[i] Exit code: ${code} (${EXIT_CODE_DESCRIPTIONS[code] || 'Unknown'})`);
|
|
}
|
|
|
|
process.exit(code);
|
|
}
|
|
|
|
/**
|
|
* Exit with success
|
|
* Runs cleanup and exits with SUCCESS code
|
|
*
|
|
* @param message - Optional success message to display
|
|
* @returns never - Always exits the process
|
|
*/
|
|
export function exitWithSuccess(message?: string): never {
|
|
runCleanup();
|
|
if (message) {
|
|
console.log(`[OK] ${message}`);
|
|
}
|
|
process.exit(ExitCode.SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* Create a wrapped error handler for async operations
|
|
* Wraps an async function to catch and handle errors
|
|
*/
|
|
export function withErrorHandling<T extends unknown[]>(
|
|
fn: (...args: T) => Promise<void>
|
|
): (...args: T) => Promise<void> {
|
|
return async (...args: T): Promise<void> => {
|
|
try {
|
|
await fn(...args);
|
|
} catch (error) {
|
|
handleError(error);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Assert a condition, throwing a CCSError if false
|
|
*/
|
|
export function assertOrExit(
|
|
condition: boolean,
|
|
message: string,
|
|
code: ExitCode = ExitCode.GENERAL_ERROR
|
|
): asserts condition {
|
|
if (!condition) {
|
|
exitWithError(message, code);
|
|
}
|
|
}
|