import Foundation public struct SessionMetadata: Equatable, Sendable { public let instanceID: UUID public let authenticated: Bool public let checkedAt: Date public init(instanceID: UUID, authenticated: Bool, checkedAt: Date) { self.instanceID = instanceID self.authenticated = authenticated self.checkedAt = checkedAt } } public struct InstanceSessionService: Sendable { private let store: InstanceSessionStore private let client: any SimAdminClientProtocol public init(store: InstanceSessionStore, client: any SimAdminClientProtocol) { self.store = store self.client = client } public func login(_ instance: Instance, password: String) async throws -> SessionMetadata { do { let response = try await client.send( .authLogin(password: password), baseURL: instance.origin, cookie: nil ) if let cookie = Self.sessionCookie(response.setCookie) { await store.set(instance.id, origin: instance.origin, cookie: cookie) return SessionMetadata(instanceID: instance.id, authenticated: true, checkedAt: Date()) } } catch { // Rejected credentials fall through to the same explicit unauthenticated state. } await store.clear(instance.id) return SessionMetadata(instanceID: instance.id, authenticated: false, checkedAt: Date()) } public func logout(_ instance: Instance) async throws -> SessionMetadata { if let cookie = await store.cookie(for: instance.id, origin: instance.origin) { _ = try? await client.send(.authLogout, baseURL: instance.origin, cookie: cookie) } await store.clear(instance.id) return SessionMetadata(instanceID: instance.id, authenticated: false, checkedAt: Date()) } public func cookie(for instance: Instance) async -> String? { await store.cookie(for: instance.id, origin: instance.origin) } static func sessionCookie(_ header: String?) -> String? { guard let header, let range = header.range(of: "simadmin_session=([^;\\s,]+)", options: .regularExpression) else { return nil } return String(header[range]) } }