CxWebApp/swift-app/Sources/CxWebAppMac/HealthMonitor.swift
CxAI Agent 055e350108
Some checks are pending
build-and-push / image (push) Waiting to run
feat: initial CxWebApp (macOS shell + swift-app wired to CxLLM-SDK)
2026-05-16 14:32:01 -05:00

56 lines
1.8 KiB
Swift

import Foundation
import Combine
/// Polls /api/health on the C++ backend. Drives the menu-bar indicator.
@MainActor
final class HealthMonitor: ObservableObject {
enum Status { case unknown, up, down }
@Published private(set) var status: Status = .unknown
@Published private(set) var lastChecked: Date?
@Published private(set) var lastError: String?
private var timer: Timer?
private let session = URLSession(configuration: .ephemeral)
func start(url: String, every interval: TimeInterval = 10) {
timer?.invalidate()
check(url: url)
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
Task { @MainActor in self?.check(url: url) }
}
}
func stop() { timer?.invalidate(); timer = nil }
func check(url: String) {
guard let base = URL(string: url),
let healthURL = URL(string: "/api/health", relativeTo: base) else {
status = .down
lastError = "invalid url: \(url)"
return
}
var req = URLRequest(url: healthURL)
req.timeoutInterval = 5
session.dataTask(with: req) { [weak self] _, resp, err in
Task { @MainActor in
guard let self else { return }
self.lastChecked = Date()
if let err = err {
self.status = .down
self.lastError = err.localizedDescription
return
}
let code = (resp as? HTTPURLResponse)?.statusCode ?? 0
if (200..<300).contains(code) {
self.status = .up
self.lastError = nil
} else {
self.status = .down
self.lastError = "HTTP \(code)"
}
}
}.resume()
}
}