feat(macos): show cron scheduler status

This commit is contained in:
Peter Steinberger
2025-12-13 03:43:51 +00:00
parent d5db20c296
commit 4938fbffa8
2 changed files with 53 additions and 0 deletions

View File

@@ -10,6 +10,10 @@ final class CronJobsStore: ObservableObject {
@Published var selectedJobId: String?
@Published var runEntries: [CronRunLogEntry] = []
@Published var schedulerEnabled: Bool?
@Published var schedulerStorePath: String?
@Published var schedulerNextWakeAtMs: Int?
@Published var isLoadingJobs = false
@Published var isLoadingRuns = false
@Published var lastError: String?
@@ -61,6 +65,11 @@ final class CronJobsStore: ObservableObject {
defer { self.isLoadingJobs = false }
do {
if let status = try? await self.fetchCronStatus() {
self.schedulerEnabled = status.enabled
self.schedulerStorePath = status.storePath
self.schedulerNextWakeAtMs = status.nextWakeAtMs
}
let data = try await self.request(
method: "cron.list",
params: ["includeDisabled": true])
@@ -205,5 +214,16 @@ final class CronJobsStore: ObservableObject {
let rawParams = params?.reduce(into: [String: AnyCodable]()) { $0[$1.key] = AnyCodable($1.value) }
return try await GatewayConnection.shared.request(method: method, params: rawParams, timeoutMs: timeoutMs)
}
private func fetchCronStatus() async throws -> CronStatusResponse {
let data = try await self.request(method: "cron.status", params: nil)
return try JSONDecoder().decode(CronStatusResponse.self, from: data)
}
}
private struct CronStatusResponse: Decodable {
let enabled: Bool
let storePath: String
let jobs: Int
let nextWakeAtMs: Int?
}

View File

@@ -15,6 +15,7 @@ struct CronSettings: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
self.header
self.schedulerBanner
self.content
Spacer(minLength: 0)
}
@@ -57,6 +58,38 @@ struct CronSettings: View {
}
}
private var schedulerBanner: some View {
Group {
if self.store.schedulerEnabled == false {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 8) {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundStyle(.orange)
Text("Cron scheduler is disabled")
.font(.headline)
Spacer()
}
Text("Jobs are saved, but they will not run automatically until `cron.enabled` is set to `true` and the Gateway restarts.")
.font(.footnote)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
if let storePath = self.store.schedulerStorePath, !storePath.isEmpty {
Text(storePath)
.font(.caption.monospaced())
.foregroundStyle(.secondary)
.textSelection(.enabled)
.lineLimit(1)
.truncationMode(.middle)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(10)
.background(Color.orange.opacity(0.10))
.cornerRadius(8)
}
}
}
private var header: some View {
HStack(alignment: .top) {
VStack(alignment: .leading, spacing: 4) {