const std = @import("std"); const colors = @import("utils/colors.zig"); const ws = @import("net/ws.zig"); const crypto = @import("utils/crypto.zig"); /// User-friendly error types for CLI pub const CLIError = error{ // Configuration errors ConfigNotFound, ConfigInvalid, APIKeyMissing, APIKeyInvalid, // Network errors ConnectionFailed, ServerUnreachable, AuthenticationFailed, RequestTimeout, // Command errors InvalidArguments, MissingCommit, CommandFailed, JobNotFound, PermissionDenied, ResourceExists, JobAlreadyRunning, JobCancelled, ServerError, SyncFailed, // System errors OutOfMemory, FileSystemError, ProcessError, }; /// Error message mapping pub const ErrorMessages = struct { pub fn getMessage(err: anyerror) []const u8 { return switch (err) { // Configuration errors error.ConfigNotFound => "Configuration file not found. Run 'ml init' to create one.", error.ConfigInvalid => "Configuration file is invalid. Please check your settings.", error.APIKeyMissing => "API key not configured. Set it in your config file.", error.APIKeyInvalid => "API key is invalid. Please check your credentials.", error.InvalidAPIKeyFormat => "API key format is invalid. Expected 64-character hash.", // Network errors error.ConnectionFailed => "Failed to connect to server. Check if the server is running.", error.ServerUnreachable => "Server is unreachable. Verify network connectivity.", error.AuthenticationFailed => "Authentication failed. Check your API key.", error.RequestTimeout => "Request timed out. Server may be busy.", // Command errors error.InvalidArguments => "Invalid command arguments. Use --help for usage.", error.MissingCommit => "Missing commit ID. Use --commit to specify the commit.", error.CommandFailed => "Command failed. Check server logs for details.", error.JobNotFound => "Job not found. Verify the job name.", error.PermissionDenied => "Permission denied. Check your user permissions.", error.ResourceExists => "Resource already exists.", error.JobAlreadyRunning => "Job is already running.", error.JobCancelled => "Job was cancelled.", error.PruneFailed => "Prune operation failed. Check server logs for details.", error.ServerError => "Server error occurred. Check server logs for details.", error.SyncFailed => "Sync operation failed.", // System errors error.OutOfMemory => "Out of memory. Close other applications and try again.", error.FileSystemError => "File system error. Check disk space and permissions.", error.ProcessError => "Process error. Try again or contact support.", // WebSocket specific errors error.InvalidURL => "Invalid server URL. Check your configuration.", error.TLSNotSupported => "TLS (HTTPS) not supported in this build.", error.ConnectionRefused => "Connection refused. Server may not be running.", error.NetworkUnreachable => "Network unreachable. Check your internet connection.", error.InvalidFrame => "Invalid WebSocket frame. Protocol error.", error.EndpointNotFound => "WebSocket endpoint not found. Server may not be running or is misconfigured.", error.ServerUnavailable => "Server is temporarily unavailable.", error.HandshakeFailed => "WebSocket handshake failed.", // Default fallback else => "An unexpected error occurred. Please try again or contact support.", }; } /// Check if error is user-fixable pub fn isUserFixable(err: anyerror) bool { return switch (err) { error.ConfigNotFound, error.ConfigInvalid, error.APIKeyMissing, error.APIKeyInvalid, error.InvalidArguments, error.MissingCommit, error.JobNotFound, error.ResourceExists, error.JobAlreadyRunning, error.JobCancelled, error.SyncFailed, => true, error.ConnectionFailed, error.ServerUnreachable, error.AuthenticationFailed, error.RequestTimeout, error.ConnectionRefused, error.NetworkUnreachable, => true, else => false, }; } /// Get suggestion for fixing the error pub fn getSuggestion(err: anyerror) ?[]const u8 { return switch (err) { error.ConfigNotFound => "Run 'ml init' to create a configuration file.", error.APIKeyMissing => "Add your API key to the configuration file.", error.ConnectionFailed => "Start the API server with 'api-server' or check if it's running.", error.AuthenticationFailed => "Verify your API key in the configuration.", error.InvalidArguments => "Use 'ml --help' for correct usage.", error.MissingCommit => "Use --commit to specify the commit ID for your job.", error.JobNotFound => "List available jobs with 'ml status'.", error.ResourceExists => "Use a different name or remove the existing resource.", error.JobAlreadyRunning => "Wait for the current job to finish or cancel it first.", error.JobCancelled => "The job was cancelled. You can restart it if needed.", error.SyncFailed => "Check network connectivity and server status.", else => null, }; } }; /// Error handler for CLI commands pub const ErrorHandler = struct { /// Send crash report to server for non-user-fixable errors fn sendCrashReport() void { return; } pub fn display(self: ErrorHandler, err: anyerror, context: ?[]const u8) void { _ = self; // Self not used in current implementation const message = ErrorMessages.getMessage(err); const suggestion = ErrorMessages.getSuggestion(err); colors.printError("Error: {s}\n", .{message}); if (context) |ctx| { colors.printWarning("Context: {s}\n", .{ctx}); } if (suggestion) |sug| { colors.printInfo("Suggestion: {s}\n", .{sug}); } if (ErrorMessages.isUserFixable(err)) { colors.printInfo("This error can be fixed by updating your configuration.\n", .{}); } } pub fn handleCommandError(err: anyerror, command: []const u8) void { _ = command; // TODO: Use command in crash report // Send crash report for non-user-fixable errors sendCrashReport(); const message = ErrorMessages.getMessage(err); const suggestion = ErrorMessages.getSuggestion(err); const is_fixable = ErrorMessages.isUserFixable(err); colors.printError("Error: {s}\n", .{message}); if (suggestion) |sug| { colors.printInfo("Suggestion: {s}\n", .{sug}); } if (is_fixable) { colors.printInfo("This is a user-fixable issue.\n", .{}); } else { colors.printWarning("If this persists, check server logs or contact support.\n", .{}); } // Exit with appropriate code std.process.exit(if (is_fixable) 2 else 1); } pub fn handleNetworkError(err: anyerror, operation: []const u8) void { std.debug.print("Network error during {s}: {s}\n", .{ operation, ErrorMessages.getMessage(err) }); if (ErrorMessages.getSuggestion(err)) |sug| { std.debug.print("Try: {s}\n", .{sug}); } std.process.exit(3); } pub fn handleConfigError(err: anyerror) void { std.debug.print("Configuration error: {s}\n", .{ErrorMessages.getMessage(err)}); if (ErrorMessages.getSuggestion(err)) |sug| { std.debug.print("Fix: {s}\n", .{sug}); } std.process.exit(4); } };