Expand onboarding and adaptive keyboard intelligence

Add resilient usage analytics, OOBE gateway flows, clipboard semantic ranking, purchase recovery, style learning, and managed current-information search.
This commit is contained in:
Rocky
2026-08-22 16:33:18 +08:00
parent ac374631ae
commit e5a83843db
162 changed files with 23574 additions and 1013 deletions
@@ -0,0 +1,77 @@
// OOBEGatewayGrantCoordinator.swift
// OSGKeyboard · Shared
//
// Routes anonymous onboarding requests through a credential slot that is
// isolated from signed-in account grants.
import Foundation
public actor OOBEGatewayGrantCoordinator {
public static let allowedScopes: Set<ManagedGatewayCapability> = [
.polish,
.assistant
]
private let grants: GatewayGrantCoordinator
private let store: any GatewayGrantCredentialStore
private let now: @Sendable () -> Date
private let practiceSession: @Sendable () -> OOBEPracticeSession?
public init(
baseURL: URL = GatewayGrantCoordinator.defaultBaseURL,
store: any GatewayGrantCredentialStore = OOBEGatewayGrantKeychainStore(),
session: URLSession = .shared,
now: @escaping @Sendable () -> Date = Date.init,
practiceSession: @escaping @Sendable () -> OOBEPracticeSession? = {
KeyboardSetupBridge.activeOOBEPracticeSession
}
) {
self.store = store
self.now = now
self.practiceSession = practiceSession
self.grants = GatewayGrantCoordinator(
baseURL: baseURL,
store: store,
session: session,
refreshPath: "v1/oobe/grants/refresh",
now: now,
accountAccessPolicy: UnrestrictedManagedGatewayAccountAccessPolicy()
)
}
/// Host-only provisioning handoff after App Attest succeeds.
public func install(_ credentials: ManagedGatewayGrantCredentials) async throws {
guard credentials.scopes == Self.allowedScopes,
credentials.hasUsableRefreshToken(at: now()),
credentials.refreshExpiresAt
<= credentials.receivedAt.addingTimeInterval(30 * 60 + 1) else {
throw ManagedGatewayError.invalidGrant
}
try await store.save(credentials)
}
public func accessToken(
for capability: ManagedGatewayCapability,
feature: ManagedGatewayOOBEFeature,
forceRefresh: Bool = false
) async throws -> String {
guard Self.allowedScopes.contains(capability),
feature.requiredCapability == capability else {
throw ManagedGatewayError.scopeNotGranted(capability)
}
guard let practice = practiceSession(),
practice.isActive(at: now()),
practice.expectedFeature == feature else {
try? await clearGrant()
throw ManagedGatewayError.missingGrant
}
return try await grants.accessToken(
for: capability,
forceRefresh: forceRefresh
)
}
public func clearGrant() async throws {
try await grants.clearGrant()
}
}