This commit is contained in:
Peter Steinberger 2025-06-19 14:31:34 +02:00
parent 978205da76
commit e281ce7d72
8 changed files with 30 additions and 32 deletions

View file

@ -12,13 +12,13 @@ final class DockIconManager: NSObject {
private static let _shared = DockIconManager() private static let _shared = DockIconManager()
static var shared: DockIconManager { static var shared: DockIconManager {
return _shared _shared
} }
private var windowsObservation: NSKeyValueObservation? private var windowsObservation: NSKeyValueObservation?
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "VibeTunnel", category: "DockIconManager") private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "VibeTunnel", category: "DockIconManager")
private override init() { override private init() {
super.init() super.init()
setupObservers() setupObservers()
// Initial update after a small delay to ensure app state is ready // Initial update after a small delay to ensure app state is ready

View file

@ -53,7 +53,7 @@ final class AppleScriptPermissionManager: ObservableObject {
/// This returns the cached state which may not be 100% accurate if user changed /// This returns the cached state which may not be 100% accurate if user changed
/// permissions in System Preferences, but avoids triggering the dialog. /// permissions in System Preferences, but avoids triggering the dialog.
func checkPermissionStatus() -> Bool { func checkPermissionStatus() -> Bool {
return hasPermission hasPermission
} }
/// Performs a silent permission check that won't trigger the dialog. /// Performs a silent permission check that won't trigger the dialog.

View file

@ -243,11 +243,14 @@ final class RustServer: ServerProtocol {
do { do {
try await processHandler.runProcess(process) try await processHandler.runProcess(process)
isRunning = true await MainActor.run {
self.isRunning = true
// Immediately check for early exit (e.g., port binding failure) }
try await Task.sleep(for: .milliseconds(100))
// Check for early exit on background thread
try await Task.detached(priority: .userInitiated) {
try await Task.sleep(for: .milliseconds(100))
}.value
// Try to read any immediate error output // Try to read any immediate error output
if let stderrPipe = self.stderrPipe { if let stderrPipe = self.stderrPipe {
let errorHandle = stderrPipe.fileHandleForReading let errorHandle = stderrPipe.fileHandleForReading
@ -261,7 +264,7 @@ final class RustServer: ServerProtocol {
// Extract port number if possible // Extract port number if possible
let portPattern = #"Address already in use.*?(\d+)"# let portPattern = #"Address already in use.*?(\d+)"#
if let regex = try? NSRegularExpression(pattern: portPattern), if let regex = try? NSRegularExpression(pattern: portPattern),
let match = regex.firstMatch(in: errorString, range: NSRange(errorString.startIndex..., in: errorString)) { regex.firstMatch(in: errorString, range: NSRange(errorString.startIndex..., in: errorString)) != nil {
// Port conflict detected // Port conflict detected
logContinuation?.yield(ServerLogEntry( logContinuation?.yield(ServerLogEntry(
level: .error, level: .error,
@ -283,8 +286,10 @@ final class RustServer: ServerProtocol {
} }
} }
// Give the server more time to fully start // Give the server more time to fully start on background thread
try await Task.sleep(for: .milliseconds(900)) try await Task.detached(priority: .userInitiated) {
try await Task.sleep(for: .milliseconds(900))
}.value
// Check if process is still running // Check if process is still running
if !process.isRunning { if !process.isRunning {
@ -316,10 +321,6 @@ final class RustServer: ServerProtocol {
} }
} }
// Log command that failed
logger.error("Failed command: /bin/zsh -l -c \"\(ttyFwdCommand)\"")
errorDetails += "\nCommand: \(ttyFwdCommand)"
logContinuation?.yield(ServerLogEntry( logContinuation?.yield(ServerLogEntry(
level: .error, level: .error,
message: "Server process failed to start - \(errorDetails)", message: "Server process failed to start - \(errorDetails)",

View file

@ -557,7 +557,7 @@ class ServerManager {
extension PortConflictError { extension PortConflictError {
static func portInUseByApp(appName: String, port: Int, alternatives: [Int]) -> Error { static func portInUseByApp(appName: String, port: Int, alternatives: [Int]) -> Error {
return NSError( NSError(
domain: "com.steipete.VibeTunnel.ServerManager", domain: "com.steipete.VibeTunnel.ServerManager",
code: 1001, code: 1001,
userInfo: [ userInfo: [

View file

@ -1857,20 +1857,18 @@ public final class TunnelServer {
let sessions = try? JSONDecoder().decode([String: TtyFwdSession].self, from: sessionData) let sessions = try? JSONDecoder().decode([String: TtyFwdSession].self, from: sessionData)
{ {
// Start streaming for new sessions // Start streaming for new sessions
for (sessionId, _) in sessions { for (sessionId, _) in sessions where !activeSessions.contains(sessionId) {
if !activeSessions.contains(sessionId) { activeSessions.insert(sessionId)
activeSessions.insert(sessionId) logger.info("Starting stream for new session: \(sessionId)")
logger.info("Starting stream for new session: \(sessionId)")
// Create task for this session // Create task for this session
let task = Task { let task = Task {
await self.streamSessionForMultiplex( await self.streamSessionForMultiplex(
sessionId: sessionId, sessionId: sessionId,
continuation: continuation continuation: continuation
) )
}
await sessionTasks.add(sessionId: sessionId, task: task)
} }
await sessionTasks.add(sessionId: sessionId, task: task)
} }
// Clean up completed sessions // Clean up completed sessions

View file

@ -22,7 +22,7 @@ struct ProcessDetails {
/// Check if this is one of our managed servers /// Check if this is one of our managed servers
var isManagedServer: Bool { var isManagedServer: Bool {
return name == "tty-fwd" || name.contains("node") && (path?.contains("VibeTunnel") ?? false) name == "tty-fwd" || name.contains("node") && (path?.contains("VibeTunnel") ?? false)
} }
} }

View file

@ -127,7 +127,7 @@ struct WelcomeView: View {
welcomeVersion = AppConstants.currentWelcomeVersion welcomeVersion = AppConstants.currentWelcomeVersion
// Close the window properly through the window controller // Close the window properly through the window controller
if let window = NSApp.windows.first(where: { $0.contentViewController is NSHostingController<WelcomeView> }) { if let window = NSApp.windows.first(where: { $0.contentViewController is NSHostingController<Self> }) {
window.close() window.close()
} }

View file

@ -89,7 +89,6 @@ final class WelcomeWindowController: NSWindowController, NSWindowDelegate {
try? await Task.sleep(for: .milliseconds(100)) try? await Task.sleep(for: .milliseconds(100))
} }
} }
} }
// MARK: - Notification Extension // MARK: - Notification Extension