1121 lines · 43.4 KB · Swift
RawDownload
import Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endif // swiftlint:disable type_body_lengthpublic struct AlibabaCodingPlanUsageFetcher: Sendable {    private static let log = CodexBarLog.logger("alibaba-coding-plan")    private static let browserLikeUserAgent =        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"    private static let safariLikeUserAgent =        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +        "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15"     enum AuthMode {        case apiKey        case webSession    }     public static func fetchUsage(        apiKey: String,        region: AlibabaCodingPlanAPIRegion = .international,        environment: [String: String] = ProcessInfo.processInfo.environment,        now: Date = Date(),        transport: any ProviderHTTPTransport = ProviderHTTPClient.shared) async throws -> AlibabaCodingPlanUsageSnapshot    {        let cleanedKey = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)        guard !cleanedKey.isEmpty else {            throw AlibabaCodingPlanUsageError.invalidCredentials        }        if let rejectedKey = AlibabaCodingPlanSettingsReader.rejectedEndpointOverrideKey(environment: environment) {            throw ProviderEndpointOverrideError.alibabaCodingPlan(rejectedKey)        }         if region != .international {            return try await self.fetchUsageOnce(                apiKey: cleanedKey,                region: region,                environment: environment,                now: now,                transport: transport)        }         do {            return try await self.fetchUsageOnce(                apiKey: cleanedKey,                region: .international,                environment: environment,                now: now,                transport: transport)        } catch let error as AlibabaCodingPlanUsageError {            guard error.shouldRetryOnAlternateRegion else { throw error }            Self.log.debug("Alibaba Coding Plan request failed on intl host; retrying cn host")            return try await self.fetchUsageOnce(                apiKey: cleanedKey,                region: .chinaMainland,                environment: environment,                now: now,                transport: transport)        }    }     public static func fetchUsage(        cookieHeader: String,        region: AlibabaCodingPlanAPIRegion = .international,        environment: [String: String] = ProcessInfo.processInfo.environment,        now: Date = Date(),        transport: any ProviderHTTPTransport = ProviderHTTPClient.shared) async throws -> AlibabaCodingPlanUsageSnapshot    {        guard let normalizedCookie = CookieHeaderNormalizer.normalize(cookieHeader) else {            throw AlibabaCodingPlanSettingsError.invalidCookie        }        if let rejectedKey = AlibabaCodingPlanSettingsReader.rejectedEndpointOverrideKey(environment: environment) {            throw ProviderEndpointOverrideError.alibabaCodingPlan(rejectedKey)        }         if region != .international {            return try await self.fetchUsageOnce(                cookieHeader: normalizedCookie,                region: region,                environment: environment,                now: now,                transport: transport)        }         do {            return try await self.fetchUsageOnce(                cookieHeader: normalizedCookie,                region: .international,                environment: environment,                now: now,                transport: transport)        } catch let error as AlibabaCodingPlanUsageError {            guard error.shouldRetryOnAlternateRegion else { throw error }            Self.log.debug("Alibaba Coding Plan cookie request failed on intl host; retrying cn host")            return try await self.fetchUsageOnce(                cookieHeader: normalizedCookie,                region: .chinaMainland,                environment: environment,                now: now,                transport: transport)        }    }     private static func fetchUsageOnce(        apiKey: String,        region: AlibabaCodingPlanAPIRegion,        environment: [String: String],        now: Date,        transport: any ProviderHTTPTransport) async throws -> AlibabaCodingPlanUsageSnapshot    {        let url = self.resolveQuotaURL(region: region, environment: environment)        var request = URLRequest(url: url)        request.httpMethod = "POST"        request.httpBody = self.queryCodingPlanAPIRequestBody(region: region)        request.setValue("application/json", forHTTPHeaderField: "Content-Type")        request.setValue("application/json", forHTTPHeaderField: "Accept")        request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")        request.setValue(apiKey, forHTTPHeaderField: "x-api-key")        request.setValue(apiKey, forHTTPHeaderField: "X-DashScope-API-Key")        request.setValue(Self.browserLikeUserAgent, forHTTPHeaderField: "User-Agent")        request.setValue(region.gatewayBaseURLString, forHTTPHeaderField: "Origin")        request.setValue(region.dashboardURL.absoluteString, forHTTPHeaderField: "Referer")         let response = try await transport.response(for: request)        let data = response.data        guard response.statusCode == 200 else {            if response.statusCode == 401 || response.statusCode == 403 {                throw AlibabaCodingPlanUsageError.invalidCredentials            }            let body = String(data: data, encoding: .utf8) ?? ""            Self.log.error("Alibaba Coding Plan returned \(response.statusCode): \(body)")            throw AlibabaCodingPlanUsageError.apiError("HTTP \(response.statusCode)")        }         return try self.parseUsageSnapshot(from: data, now: now, authMode: .apiKey)    }     private static func fetchUsageOnce(        cookieHeader: String,        region: AlibabaCodingPlanAPIRegion,        environment: [String: String],        now: Date,        transport: any ProviderHTTPTransport) async throws -> AlibabaCodingPlanUsageSnapshot    {        let url = self.resolveConsoleQuotaURL(region: region, environment: environment)        let secToken = try await self.resolveConsoleSECToken(            cookieHeader: cookieHeader,            region: region,            environment: environment,            transport: transport)        let anonymousID = self.extractCookieValue(name: "cna", from: cookieHeader)         var request = URLRequest(url: url)        request.httpMethod = "POST"        request.httpBody = self.queryCodingPlanConsoleRequestBody(            region: region,            secToken: secToken,            anonymousID: anonymousID)        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")        request.setValue("*/*", forHTTPHeaderField: "Accept")        request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")        if let csrf = self.extractCookieValue(name: "login_aliyunid_csrf", from: cookieHeader) ??            self.extractCookieValue(name: "csrf", from: cookieHeader)        {            request.setValue(csrf, forHTTPHeaderField: "x-xsrf-token")            request.setValue(csrf, forHTTPHeaderField: "x-csrf-token")        }        request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")        request.setValue(Self.browserLikeUserAgent, forHTTPHeaderField: "User-Agent")        request.setValue(region.gatewayBaseURLString, forHTTPHeaderField: "Origin")        request.setValue(region.consoleRefererURL.absoluteString, forHTTPHeaderField: "Referer")         let response = try await transport.response(for: request)        let data = response.data        guard response.statusCode == 200 else {            if response.statusCode == 401 || response.statusCode == 403 {                throw AlibabaCodingPlanUsageError.loginRequired            }            let body = String(data: data, encoding: .utf8) ?? ""            Self.log.error("Alibaba Coding Plan returned \(response.statusCode): \(body)")            throw AlibabaCodingPlanUsageError.apiError("HTTP \(response.statusCode)")        }         return try self.parseUsageSnapshot(from: data, now: now, authMode: .webSession)    }     private static func queryCodingPlanAPIRequestBody(region: AlibabaCodingPlanAPIRegion) -> Data {        let payload: [String: Any] = [            "queryCodingPlanInstanceInfoRequest": [                "commodityCode": region.commodityCode,            ],        ]        return (try? JSONSerialization.data(withJSONObject: payload, options: [])) ?? Data("{}".utf8)    }     private static func queryCodingPlanConsoleRequestBody(        region: AlibabaCodingPlanAPIRegion,        secToken: String,        anonymousID: String?) -> Data    {        let traceID = UUID().uuidString.lowercased()        var cornerstoneParam: [String: Any] = [            "feTraceId": traceID,            "feURL": region.dashboardURL.absoluteString,            "protocol": "V2",            "console": "ONE_CONSOLE",            "productCode": "p_efm",            "domain": region.consoleDomain,            "consoleSite": region.consoleSite,            "userNickName": "",            "userPrincipalName": "",            "xsp_lang": "en-US",        ]        if let anonymousID, !anonymousID.isEmpty {            cornerstoneParam["X-Anonymous-Id"] = anonymousID        }         let paramsObject: [String: Any] = [            "Api": region.consoleQuotaAPIName,            "V": "1.0",            "Data": [                "queryCodingPlanInstanceInfoRequest": [                    "commodityCode": region.commodityCode,                    "onlyLatestOne": true,                ],                "cornerstoneParam": cornerstoneParam,            ],        ]         guard let paramsData = try? JSONSerialization.data(withJSONObject: paramsObject, options: []),              let paramsString = String(data: paramsData, encoding: .utf8)        else {            return Data()        }         var components = URLComponents()        components.queryItems = [            URLQueryItem(name: "params", value: paramsString),            URLQueryItem(name: "region", value: region.currentRegionID),            URLQueryItem(name: "sec_token", value: secToken),        ]        return Data((components.percentEncodedQuery ?? "").utf8)    }     static func resolveConsoleQuotaURL(        region: AlibabaCodingPlanAPIRegion,        environment: [String: String]) -> URL    {        if let override = AlibabaCodingPlanSettingsReader.quotaURL(environment: environment) {            return override        }        if let host = AlibabaCodingPlanSettingsReader.hostOverride(environment: environment),           let hostURL = self.consoleURL(from: host, region: region)        {            return hostURL        }        return region.consoleRPCURL    }     static func resolveQuotaURL(        region: AlibabaCodingPlanAPIRegion,        environment: [String: String]) -> URL    {        if let override = AlibabaCodingPlanSettingsReader.quotaURL(environment: environment) {            return override        }        if let host = AlibabaCodingPlanSettingsReader.hostOverride(environment: environment),           let hostURL = self.url(from: host, region: region)        {            return hostURL        }        return region.quotaURL    }     static func resolveConsoleDashboardURL(        region: AlibabaCodingPlanAPIRegion,        environment: [String: String]) -> URL    {        if let override = AlibabaCodingPlanSettingsReader.hostOverride(environment: environment),           let hostURL = self.dashboardURL(from: override, region: region)        {            return hostURL        }        return region.dashboardURL    }     static func url(from rawHost: String, region: AlibabaCodingPlanAPIRegion) -> URL? {        let cleaned = AlibabaCodingPlanSettingsReader.cleaned(rawHost)        guard let cleaned else { return nil }         let base = ProviderEndpointOverrideValidator.normalizedHTTPSURL(from: cleaned)        guard let base else { return nil }         var components = URLComponents(url: base, resolvingAgainstBaseURL: false)        components?.path = "/data/api.json"        components?.queryItems = [            URLQueryItem(                name: "action",                value: "zeldaEasy.broadscope-bailian.codingPlan.queryCodingPlanInstanceInfoV2"),            URLQueryItem(name: "product", value: "broadscope-bailian"),            URLQueryItem(name: "api", value: "queryCodingPlanInstanceInfoV2"),            URLQueryItem(name: "currentRegionId", value: region.currentRegionID),        ]        return components?.url    }     static func consoleURL(from rawHost: String, region: AlibabaCodingPlanAPIRegion) -> URL? {        let cleaned = AlibabaCodingPlanSettingsReader.cleaned(rawHost)        guard let cleaned else { return nil }         let base = ProviderEndpointOverrideValidator.normalizedHTTPSURL(from: cleaned)        guard let base else { return nil }         var components = URLComponents(url: base, resolvingAgainstBaseURL: false)        components?.path = "/data/api.json"        components?.queryItems = [            URLQueryItem(name: "action", value: region.consoleRPCAction),            URLQueryItem(name: "product", value: region.consoleRPCProduct),            URLQueryItem(name: "api", value: region.consoleQuotaAPIName),            URLQueryItem(name: "_v", value: "undefined"),        ]        return components?.url    }     private static func resolveConsoleSECToken(        cookieHeader: String,        region: AlibabaCodingPlanAPIRegion,        environment: [String: String],        transport: any ProviderHTTPTransport) async throws -> String    {        let cookieSECToken = self.extractCookieValue(name: "sec_token", from: cookieHeader)         let dashboardURL = self.resolveConsoleDashboardURL(region: region, environment: environment)         var request = URLRequest(url: dashboardURL)        request.httpMethod = "GET"        request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")        request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent")        request.setValue(            "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",            forHTTPHeaderField: "Accept")         if let response = try? await transport.response(for: request),           response.statusCode == 200,           let html = String(data: response.data, encoding: .utf8),           let token = self.extractConsoleSECToken(from: html),           !token.isEmpty        {            return token        }         if let token = try? await self.fetchSECTokenFromUserInfo(            cookieHeader: cookieHeader,            region: region,            environment: environment,            transport: transport)        {            return token        }         if let cookieSECToken, !cookieSECToken.isEmpty {            return cookieSECToken        }         throw AlibabaCodingPlanUsageError.loginRequired    }     static func dashboardURL(from rawHost: String, region: AlibabaCodingPlanAPIRegion) -> URL? {        let cleaned = AlibabaCodingPlanSettingsReader.cleaned(rawHost)        guard let cleaned else { return nil }         let base = ProviderEndpointOverrideValidator.normalizedHTTPSURL(from: cleaned)        guard let base else { return nil }         guard var components = URLComponents(url: base, resolvingAgainstBaseURL: false),              let dashboardComponents = URLComponents(                  url: region.dashboardURL,                  resolvingAgainstBaseURL: false)        else {            return nil        }         components.path = dashboardComponents.path        components.percentEncodedQuery = dashboardComponents.percentEncodedQuery        components.fragment = dashboardComponents.fragment        return components.url    }     private static func fetchSECTokenFromUserInfo(        cookieHeader: String,        region: AlibabaCodingPlanAPIRegion,        environment: [String: String],        transport: any ProviderHTTPTransport) async throws -> String?    {        let gatewayBaseURL = self.resolveConsoleGatewayBaseURL(region: region, environment: environment)        let userInfoURL = gatewayBaseURL.appendingPathComponent("tool/user/info.json")        var request = URLRequest(url: userInfoURL)        request.httpMethod = "GET"        request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")        request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent")        request.setValue("application/json, text/plain, */*", forHTTPHeaderField: "Accept")        let referer = gatewayBaseURL.absoluteString.hasSuffix("/") ? gatewayBaseURL.absoluteString : gatewayBaseURL            .absoluteString + "/"        request.setValue(referer, forHTTPHeaderField: "Referer")         let response = try await transport.response(for: request)        guard response.statusCode == 200 else {            return nil        }         let object = try JSONSerialization.jsonObject(with: response.data, options: [])        let expanded = self.expandedJSON(object)        return self.findFirstString(forKeys: ["secToken", "sec_token"], in: expanded)    }     private static func resolveConsoleGatewayBaseURL(        region: AlibabaCodingPlanAPIRegion,        environment: [String: String]) -> URL    {        if let host = AlibabaCodingPlanSettingsReader.hostOverride(environment: environment),           let hostURL = self.baseURL(from: host)        {            return hostURL        }        return URL(string: region.gatewayBaseURLString)!    }     private static func baseURL(from rawHost: String) -> URL? {        let cleaned = AlibabaCodingPlanSettingsReader.cleaned(rawHost)        guard let cleaned else { return nil }         let base = ProviderEndpointOverrideValidator.normalizedHTTPSURL(from: cleaned)        guard let base else { return nil }         guard var components = URLComponents(url: base, resolvingAgainstBaseURL: false) else {            return nil        }        components.path = ""        components.query = nil        components.fragment = nil        return components.url    }     static func parseUsageSnapshot(        from data: Data,        now: Date = Date(),        authMode: AuthMode = .webSession) throws -> AlibabaCodingPlanUsageSnapshot    {        guard !data.isEmpty else {            throw AlibabaCodingPlanUsageError.parseFailed("Empty response body")        }         let object = try JSONSerialization.jsonObject(with: data, options: [])        let expanded = self.expandedJSON(object)        guard let dictionary = expanded as? [String: Any] else {            throw AlibabaCodingPlanUsageError.parseFailed("Unexpected payload")        }         if let statusCode = self.findFirstInt(forKeys: ["statusCode", "status_code", "code"], in: dictionary),           statusCode != 0,           statusCode != 200        {            let message = self.findFirstString(                forKeys: ["statusMessage", "status_msg", "message", "msg"],                in: dictionary)                ?? "status code \(statusCode)"            let lower = message.lowercased()            if statusCode == 401 || statusCode == 403 || lower.contains("api key") || lower.contains("unauthorized") {                throw AlibabaCodingPlanUsageError.invalidCredentials            }            throw AlibabaCodingPlanUsageError.apiError(message)        }         if let codeText = self.findFirstString(forKeys: ["code", "status", "statusCode"], in: dictionary) {            let normalizedCode = codeText.lowercased()            if normalizedCode.contains("needlogin") || normalizedCode.contains("login") {                if authMode == .apiKey {                    throw AlibabaCodingPlanUsageError.apiKeyUnavailableInRegion                }                throw AlibabaCodingPlanUsageError.loginRequired            }        }        if let messageText = self.findFirstString(forKeys: ["message", "msg", "statusMessage"], in: dictionary) {            let normalizedMessage = messageText.lowercased()            if normalizedMessage.contains("log in") || normalizedMessage.contains("login") {                if authMode == .apiKey {                    throw AlibabaCodingPlanUsageError.apiKeyUnavailableInRegion                }                throw AlibabaCodingPlanUsageError.loginRequired            }            if authMode == .apiKey,               normalizedMessage.contains("console session") ||               normalizedMessage.contains("api key mode may be unavailable")            {                throw AlibabaCodingPlanUsageError.apiKeyUnavailableInRegion            }        } 

Lines 1–500 of 1121.