warning fixes

This commit is contained in:
Peter Steinberger 2025-07-31 02:21:18 +02:00
parent fe2f30ed73
commit e9a1ce0555
11 changed files with 92 additions and 81 deletions

View file

@ -13,9 +13,9 @@ extension Notification.Name {
// MARK: - Welcome
static let showWelcomeScreen = Notification.Name("showWelcomeScreen")
// MARK: - Services
static let notificationServiceConnectionChanged = Notification.Name("notificationServiceConnectionChanged")
}

View file

@ -60,9 +60,9 @@ final class EventSource: NSObject {
// MARK: - Connection Management
func connect() {
guard !isConnected else {
guard !isConnected else {
logger.warning("Already connected, ignoring connect request")
return
return
}
var request = URLRequest(url: url)
@ -84,7 +84,7 @@ final class EventSource: NSObject {
dataTask = urlSession?.dataTask(with: request)
dataTask?.resume()
logger.info("📡 EventSource dataTask started")
}
@ -136,7 +136,8 @@ final class EventSource: NSObject {
}
// Dispatch event
logger.debug("🎯 Dispatching event - type: \(event.event ?? "default"), data: \(event.data ?? "none")")
logger
.debug("🎯 Dispatching event - type: \(event.event ?? "default"), data: \(event.data ?? "none")")
DispatchQueue.main.async {
self.onMessage?(event)
}
@ -197,7 +198,7 @@ extension EventSource: URLSessionDataDelegate {
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void
) {
logger.info("📥 URLSession didReceive response")
guard let httpResponse = response as? HTTPURLResponse else {
logger.error("Response is not HTTPURLResponse")
completionHandler(.cancel)
@ -224,19 +225,19 @@ extension EventSource: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
logger.debug("📨 EventSource received \(data.count) bytes of data")
// Check if data might be compressed
if data.count > 2 {
let header = [UInt8](data.prefix(2))
if header[0] == 0x1f && header[1] == 0x8b {
if header[0] == 0x1F && header[1] == 0x8B {
logger.error("❌ Received gzip compressed data! SSE should not be compressed.")
return
}
}
guard let text = String(data: data, encoding: .utf8) else {
guard let text = String(data: data, encoding: .utf8) else {
logger.error("Failed to decode data as UTF-8. First 20 bytes: \(data.prefix(20).hexString)")
return
return
}
logger.debug("📨 EventSource received text: \(text)")

View file

@ -21,7 +21,7 @@ final class NotificationService: NSObject {
private var isConnected = false
private var recentlyNotifiedSessions = Set<String>()
private var notificationCleanupTimer: Timer?
/// Public property to check SSE connection status
var isSSEConnected: Bool { isConnected }
@ -67,13 +67,13 @@ final class NotificationService: NSObject {
/// Start monitoring server events
func start() async {
logger.info("🚀 NotificationService.start() called")
// Check if notifications are enabled in config
guard configManager.notificationsEnabled else {
logger.info("📴 Notifications are disabled in config, skipping SSE connection")
return
}
guard serverManager.isRunning else {
logger.warning("🔴 Server not running, cannot start notification service")
return
@ -87,18 +87,18 @@ final class NotificationService: NSObject {
waitForUnixSocketAndConnect()
}
}
/// Wait for Unix socket ready notification then connect
private func waitForUnixSocketAndConnect() {
logger.info("⏳ Waiting for Unix socket ready notification...")
// Check if Unix socket is already connected
if SharedUnixSocketManager.shared.isConnected {
logger.info("✅ Unix socket already connected, connecting to SSE immediately")
connect()
return
}
// Listen for Unix socket ready notification
NotificationCenter.default.addObserver(
forName: SharedUnixSocketManager.unixSocketReadyNotification,
@ -108,7 +108,7 @@ final class NotificationService: NSObject {
Task { @MainActor [weak self] in
self?.logger.info("✅ Unix socket ready notification received, connecting to SSE")
self?.connect()
// Remove observer after first notification to prevent duplicate connections
NotificationCenter.default.removeObserver(
self as Any,
@ -423,7 +423,6 @@ final class NotificationService: NSObject {
// This prevents dual-path connection attempts
}
private func connect() {
logger.info("🔌 NotificationService.connect() called - isConnected: \(self.isConnected)")
guard !isConnected else {
@ -434,13 +433,14 @@ final class NotificationService: NSObject {
// When auth mode is "none", we can connect without a token.
// In any other auth mode, a token is required for the local Mac app to connect.
if serverManager.authMode != "none", serverManager.localAuthToken == nil {
logger.error("No auth token available for notification service in auth mode '\(self.serverManager.authMode)'")
logger
.error("No auth token available for notification service in auth mode '\(self.serverManager.authMode)'")
return
}
let eventsURL = "http://localhost:\(self.serverManager.port)/api/events"
logger.info("📡 Attempting to connect to SSE endpoint: \(eventsURL)")
guard let url = URL(string: eventsURL) else {
logger.error("Invalid events URL: \(eventsURL)")
return
@ -489,7 +489,10 @@ final class NotificationService: NSObject {
eventSource?.onMessage = { [weak self] event in
Task { @MainActor in
self?.logger.info("🎯 EventSource onMessage fired! Event type: \(event.event ?? "default"), Has data: \(event.data != nil)")
self?.logger
.info(
"🎯 EventSource onMessage fired! Event type: \(event.event ?? "default"), Has data: \(event.data != nil)"
)
self?.handleEvent(event)
}
}
@ -507,9 +510,9 @@ final class NotificationService: NSObject {
}
private func handleEvent(_ event: Event) {
guard let data = event.data else {
guard let data = event.data else {
logger.warning("Received event with no data")
return
return
}
// Log event details for debugging
@ -725,25 +728,25 @@ final class NotificationService: NSObject {
private func handleTestNotification(_ json: [String: Any]) {
logger.info("🧪 Handling test notification from server")
let title = json["title"] as? String ?? "VibeTunnel Test"
let body = json["body"] as? String ?? "Server-side notifications are working correctly!"
let message = json["message"] as? String
let content = UNMutableNotificationContent()
content.title = title
content.body = body
if let message = message {
if let message {
content.subtitle = message
}
content.sound = getNotificationSound()
content.categoryIdentifier = "TEST"
content.userInfo = ["type": "test-notification"]
logger.info("📤 Delivering test notification: \(title) - \(body)")
deliverNotification(content, identifier: "test-\(UUID().uuidString)")
}
private func handleClaudeTurn(_ json: [String: Any]) {
guard let sessionId = json["sessionId"] as? String else {
logger.error("Claude turn event missing sessionId")
@ -803,13 +806,13 @@ final class NotificationService: NSObject {
/// Send a test notification through the server to verify the full flow
func sendServerTestNotification() async {
logger.info("🧪 Sending test notification through server...")
// Check if server is running
guard serverManager.isRunning else {
logger.error("❌ Cannot send test notification - server is not running")
return
}
// If not connected to SSE, try to connect first
if !isConnected {
logger.warning("⚠️ Not connected to SSE endpoint, attempting to connect...")
@ -819,33 +822,36 @@ final class NotificationService: NSObject {
// Give it a moment to connect
try? await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds
}
// Log server info
logger.info("Server info - Port: \(self.serverManager.port), Running: \(self.serverManager.isRunning), SSE Connected: \(self.isConnected)")
logger
.info(
"Server info - Port: \(self.serverManager.port), Running: \(self.serverManager.isRunning), SSE Connected: \(self.isConnected)"
)
guard let url = serverManager.buildURL(endpoint: "/api/test-notification") else {
logger.error("❌ Failed to build test notification URL")
return
}
logger.info("📤 Sending POST request to: \(url)")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// Add auth token if available
if let authToken = serverManager.localAuthToken {
request.setValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization")
logger.debug("Added auth token to request")
}
do {
let (data, response) = try await URLSession.shared.data(for: request)
if let httpResponse = response as? HTTPURLResponse {
logger.info("📥 Received response - Status: \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 {
logger.info("✅ Server test notification sent successfully")
if let responseData = String(data: data, encoding: .utf8) {
@ -870,4 +876,3 @@ final class NotificationService: NSObject {
// NotificationCenter observers are automatically removed on deinit in modern Swift
}
}

View file

@ -134,7 +134,7 @@ final class SessionMonitor {
private func fetchSessions() async {
do {
// Snapshot previous sessions for exit notifications
let _ = sessions
_ = sessions
let sessionsArray = try await serverManager.performRequest(
endpoint: APIEndpoints.sessions,
@ -233,4 +233,4 @@ final class SessionMonitor {
"Pre-caching Git data for \(uniqueDirectoriesToCheck.count) unique directories (from \(sessions.count) sessions)"
)
}
}
}

View file

@ -22,9 +22,9 @@ final class SharedUnixSocketManager {
private init() {
logger.info("🚀 SharedUnixSocketManager initialized")
}
// MARK: - Notifications
static let unixSocketReadyNotification = Notification.Name("unixSocketReady")
// MARK: - Public Methods
@ -45,7 +45,7 @@ final class SharedUnixSocketManager {
self?.distributeMessage(data)
}
}
// Set up state change handler to notify when socket is ready
socket.onStateChange = { [weak self] state in
Task { @MainActor [weak self] in
@ -56,7 +56,7 @@ final class SharedUnixSocketManager {
unixSocket = socket
return socket
}
/// Handle socket state changes and notify when ready
private func handleSocketStateChange(_ state: UnixSocketConnection.ConnectionState) {
switch state {

View file

@ -74,11 +74,13 @@ final class TailscaleServeStatusService {
let decoder = JSONDecoder()
// Use custom date decoder to handle ISO8601 with fractional seconds
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
// Create formatter inside the closure to avoid Sendable warning
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = formatter.date(from: dateString) {
return date
}
@ -87,7 +89,10 @@ final class TailscaleServeStatusService {
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Cannot decode date string \(dateString)"
)
}
let status = try decoder.decode(TailscaleServeStatus.self, from: data)
@ -98,7 +103,6 @@ final class TailscaleServeStatusService {
startTime = status.startTime
logger.debug("Tailscale Serve status - Running: \(status.isRunning), Error: \(status.lastError ?? "none")")
} catch {
logger.error("Failed to fetch Tailscale Serve status: \(error.localizedDescription)")
// On error, assume not running

View file

@ -1,6 +1,6 @@
import SwiftUI
import AppKit
import os.log
import SwiftUI
/// Authentication configuration section for remote access settings
struct AuthenticationSection: View {
@ -8,7 +8,7 @@ struct AuthenticationSection: View {
@Binding var enableSSHKeys: Bool
let logger: Logger
let serverManager: ServerManager
var body: some View {
Section {
VStack(alignment: .leading, spacing: 16) {
@ -45,7 +45,7 @@ struct AuthenticationSection: View {
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
}
// Additional info based on selected mode
if authMode == .osAuth || authMode == .both {
HStack(alignment: .center, spacing: 6) {
@ -59,7 +59,7 @@ struct AuthenticationSection: View {
Spacer()
}
}
if authMode == .sshKeys || authMode == .both {
HStack(alignment: .center, spacing: 6) {
Image(systemName: "key.fill")
@ -105,7 +105,7 @@ struct AuthenticationSection: View {
struct PreviewWrapper: View {
@State var authMode = AuthenticationMode.osAuth
@State var enableSSHKeys = false
var body: some View {
AuthenticationSection(
authMode: $authMode,
@ -117,6 +117,6 @@ struct AuthenticationSection: View {
.padding()
}
}
return PreviewWrapper()
}
}

View file

@ -27,7 +27,7 @@ struct NotificationSettingsView: View {
var body: some View {
NavigationStack {
@Bindable var bindableConfig = configManager
Form {
// Master toggle section
Section {
@ -37,7 +37,7 @@ struct NotificationSettingsView: View {
.onChange(of: showNotifications) { _, newValue in
// Update ConfigManager's notificationsEnabled to match
configManager.notificationsEnabled = newValue
// Ensure NotificationService starts/stops based on the toggle
if newValue {
Task {
@ -63,7 +63,7 @@ struct NotificationSettingsView: View {
Text("Display native macOS notifications for session and command events")
.font(.caption)
.foregroundStyle(.secondary)
// SSE Connection Status Row
HStack(spacing: 6) {
Circle()
@ -78,10 +78,11 @@ struct NotificationSettingsView: View {
.fontWeight(.medium)
Spacer()
}
.help(sseConnectionStatus
? "Real-time notification stream is connected"
: "Real-time notification stream is disconnected. Check if the server is running.")
.help(sseConnectionStatus
? "Real-time notification stream is connected"
: "Real-time notification stream is disconnected. Check if the server is running."
)
// Show warning when disconnected
if showNotifications && !sseConnectionStatus {
HStack(spacing: 6) {
@ -198,7 +199,7 @@ struct NotificationSettingsView: View {
.scaleEffect(0.7)
.frame(width: 16, height: 16)
}
Spacer()
}
@ -207,7 +208,7 @@ struct NotificationSettingsView: View {
notificationService.openNotificationSettings()
}
.buttonStyle(.link)
Spacer()
}
}
@ -223,7 +224,7 @@ struct NotificationSettingsView: View {
.onAppear {
// Sync the AppStorage value with ConfigManager on first load
showNotifications = configManager.notificationsEnabled
// Update initial connection status
sseConnectionStatus = notificationService.isSSEConnected
}

View file

@ -5,7 +5,7 @@ struct PermissionsSection: View {
let hasAppleScriptPermission: Bool
let hasAccessibilityPermission: Bool
let permissionManager: SystemPermissionManager
var body: some View {
Section {
// Automation permission
@ -17,9 +17,9 @@ struct PermissionsSection: View {
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
if hasAppleScriptPermission {
HStack {
Image(systemName: "checkmark.circle.fill")
@ -47,7 +47,7 @@ struct PermissionsSection: View {
.controlSize(.small)
}
}
// Accessibility permission
HStack {
VStack(alignment: .leading, spacing: 4) {
@ -57,9 +57,9 @@ struct PermissionsSection: View {
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
if hasAccessibilityPermission {
HStack {
Image(systemName: "checkmark.circle.fill")
@ -115,7 +115,7 @@ struct PermissionsSection: View {
struct PreviewWrapper: View {
@State var hasAppleScript = true
@State var hasAccessibility = false
var body: some View {
PermissionsSection(
hasAppleScriptPermission: hasAppleScript,
@ -126,6 +126,6 @@ struct PermissionsSection: View {
.padding()
}
}
return PreviewWrapper()
}
}

View file

@ -94,7 +94,8 @@ struct RemoteAccessSettingsView: View {
onAppearSetup()
updateLocalIPAddress()
// Initialize authentication mode from stored value
let storedMode = UserDefaults.standard.string(forKey: AppConstants.UserDefaultsKeys.authenticationMode) ?? "os"
let storedMode = UserDefaults.standard
.string(forKey: AppConstants.UserDefaultsKeys.authenticationMode) ?? "os"
authMode = AuthenticationMode(rawValue: storedMode) ?? .osAuth
// Start monitoring Tailscale Serve status
tailscaleServeStatus.startMonitoring()
@ -628,7 +629,6 @@ private struct ErrorView: View {
}
}
// MARK: - Previews
#Preview("Remote Access Settings") {

View file

@ -299,7 +299,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser
statusBarController?.updateStatusItemDisplay()
// Session monitoring starts automatically
// NotificationService is started by ServerManager when the server is ready
} else {
logger.error("HTTP server failed to start")