diff --git a/app/MoleSceneView.swift b/app/MoleSceneView.swift index b94c538..7f52bc7 100644 --- a/app/MoleSceneView.swift +++ b/app/MoleSceneView.swift @@ -165,7 +165,7 @@ struct MoleSceneView: NSViewRepresentable { // Slower, majestic rotation // Auto Rotation Speed // Slower, majestic rotation normally. Fast when working. - let baseRotation = parent.isRunning ? 0.05 : 0.002 + let baseRotation = parent.isRunning ? 0.12 : 0.006 // Drag Influence let dragInfluence = Double(parent.rotationVelocity.width) * 0.0005 diff --git a/app/OptimizerService.swift b/app/OptimizerService.swift index e646052..470c969 100644 --- a/app/OptimizerService.swift +++ b/app/OptimizerService.swift @@ -24,11 +24,30 @@ class OptimizerService: ObservableObject { _ = try await ShellRunner.shared.runSudo(command, password: pw) return } catch { - await MainActor.run { AuthContext.shared.clear() } + print("Optimizer privilege error: \(error)") + // Only clear password if it's an authentication failure + if case ShellError.authenticationFailed = error { + await MainActor.run { AuthContext.shared.clear() } + + await MainActor.run { + AuthContext.shared.needsPassword = true + self.statusMessage = "Password Incorrect" + } + + struct AuthRequired: Error, LocalizedError { + var errorDescription: String? { "Authentication Failed" } + } + throw AuthRequired() + } else { + // Command failed but password likely correct. + // Do NOT clear password. Propagate error. + print("Non-auth error in optimizer: \(error)") + throw error + } } } - // If no password or failed, prompt via Custom Sheet + // If no password, prompt via Custom Sheet await MainActor.run { AuthContext.shared.needsPassword = true self.statusMessage = "Waiting for Password..." diff --git a/app/ScannerService.swift b/app/ScannerService.swift index 6a218ef..e62e89b 100644 --- a/app/ScannerService.swift +++ b/app/ScannerService.swift @@ -144,12 +144,23 @@ class ScannerService: ObservableObject { do { _ = try await ShellRunner.shared.runSudo(fullCommand, password: sessionPw) } catch { + print("Sudo command error: \(error)") print("Session password failed: \(error)") - await MainActor.run { AuthContext.shared.clear() } - // Trigger re-auth on failure + + if case ShellError.authenticationFailed = error { + await MainActor.run { + AuthContext.shared.clear() + AuthContext.shared.needsPassword = true + self.currentLog = "Password Incorrect/Expired" + self.isCleaning = false + } + return 0 + } + // Ignore other errors (e.g. command execution failed) + // but continue the flow or handle gracefully without clearing password + print("Non-auth error in cleanup: \(error)") await MainActor.run { - AuthContext.shared.needsPassword = true - self.currentLog = "Password Incorrect/Expired" + self.currentLog = "Error: \(error.localizedDescription)" self.isCleaning = false } return 0 diff --git a/app/ShellRunner.swift b/app/ShellRunner.swift index 79ac20a..0c01421 100644 --- a/app/ShellRunner.swift +++ b/app/ShellRunner.swift @@ -3,11 +3,13 @@ import Foundation enum ShellError: Error, LocalizedError { case commandFailed(output: String) case executionError(error: Error) + case authenticationFailed var errorDescription: String? { switch self { case .commandFailed(let output): return output case .executionError(let error): return error.localizedDescription + case .authenticationFailed: return "Authentication failed - incorrect password" } } } @@ -98,10 +100,15 @@ class ShellRunner { if process.terminationStatus == 0 { continuation.resume(returning: output) } else { - // If 1, it might be wrong password or command fail. - // sudo usually complains to stderr. - continuation.resume( - throwing: ShellError.commandFailed(output: errorOutput.isEmpty ? output : errorOutput)) + // Check for password failure + let combined = (output + errorOutput).lowercased() + if combined.contains("try again") || combined.contains("incorrect") { + continuation.resume(throwing: ShellError.authenticationFailed) + } else { + continuation.resume( + throwing: ShellError.commandFailed(output: errorOutput.isEmpty ? output : errorOutput) + ) + } } } @@ -117,4 +124,11 @@ class ShellRunner { } } } + + /// Escapes a string for safe use in bash commands + private func bashEscape(_ str: String) -> String { + // Use single quotes and escape any single quotes within the string + let escaped = str.replacingOccurrences(of: "'", with: "'\"'\"'") + return "'\(escaped)'" + } }