import Foundation import OSLog private let gatewayCronLogger = Logger(subsystem: "ai.openclaw", category: "gateway.connection") extension GatewayConnection { private struct LossyDecodable: Decodable { let value: Value? init(from decoder: Decoder) throws { do { self.value = try Value(from: decoder) } catch { self.value = nil } } } private struct LossyCronListResponse: Decodable { let jobs: [LossyDecodable] enum CodingKeys: String, CodingKey { case jobs } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.jobs = try container.decodeIfPresent([LossyDecodable].self, forKey: .jobs) ?? [] } } private struct LossyCronRunsResponse: Decodable { let entries: [LossyDecodable] enum CodingKeys: String, CodingKey { case entries } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.entries = try container.decodeIfPresent([LossyDecodable].self, forKey: .entries) ?? [] } } nonisolated static func decodeCronListResponse(_ data: Data) throws -> [CronJob] { let decoded = try JSONDecoder().decode(LossyCronListResponse.self, from: data) let jobs = decoded.jobs.compactMap(\.value) let skipped = decoded.jobs.count - jobs.count if skipped > 0 { gatewayCronLogger.warning("cron.list skipped \(skipped, privacy: .public) malformed jobs") } return jobs } nonisolated static func decodeCronRunsResponse(_ data: Data) throws -> [CronRunLogEntry] { let decoded = try JSONDecoder().decode(LossyCronRunsResponse.self, from: data) let entries = decoded.entries.compactMap(\.value) let skipped = decoded.entries.count - entries.count if skipped > 0 { gatewayCronLogger.warning("cron.runs skipped \(skipped, privacy: .public) malformed entries") } return entries } }