Files
2026-07-13 12:22:33 +08:00

1122 lines
43 KiB
Swift

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
// swiftlint:disable type_body_length
public 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
}
}
let instanceInfo = self.findActiveInstanceInfo(in: dictionary, now: now)
let instanceCount = self.findFirstArray(
forKeys: ["codingPlanInstanceInfos", "coding_plan_instance_infos"],
in: dictionary)?
.compactMap { $0 as? [String: Any] }
.count ?? 0
let shouldScopeQuotaToSelectedInstance =
instanceCount > 1 &&
(instanceInfo.map { self.activeSignalScore(in: $0, now: now) > 0 } ?? false)
let quota = if shouldScopeQuotaToSelectedInstance, let instanceInfo {
self.findQuotaInfo(in: instanceInfo)
} else {
self.findQuotaInfo(in: instanceInfo ?? [:]) ?? self.findQuotaInfo(in: dictionary)
}
guard let quota else {
if let fallback = self.parsePlanVisibleActiveFallback(
payload: dictionary,
instanceInfo: instanceInfo,
now: now)
{
return fallback
}
let diagnostics = self.payloadDiagnostics(payload: dictionary)
Self.log.error("Alibaba coding plan quota payload missing expected fields: \(diagnostics)")
throw AlibabaCodingPlanUsageError.parseFailed("Missing coding plan quota data (\(diagnostics))")
}
let planName = self.findPlanName(in: instanceInfo ?? [:]) ?? self.findPlanName(in: dictionary)
let snapshot = AlibabaCodingPlanUsageSnapshot(
planName: planName,
fiveHourUsedQuota: self.anyInt(for: ["per5HourUsedQuota", "perFiveHourUsedQuota"], in: quota),
fiveHourTotalQuota: self.anyInt(for: ["per5HourTotalQuota", "perFiveHourTotalQuota"], in: quota),
fiveHourNextRefreshTime: self.anyDate(
for: ["per5HourQuotaNextRefreshTime", "perFiveHourQuotaNextRefreshTime"],
in: quota),
weeklyUsedQuota: self.anyInt(for: ["perWeekUsedQuota"], in: quota),
weeklyTotalQuota: self.anyInt(for: ["perWeekTotalQuota"], in: quota),
weeklyNextRefreshTime: self.anyDate(for: ["perWeekQuotaNextRefreshTime"], in: quota),
monthlyUsedQuota: self.anyInt(for: ["perBillMonthUsedQuota", "perMonthUsedQuota"], in: quota),
monthlyTotalQuota: self.anyInt(for: ["perBillMonthTotalQuota", "perMonthTotalQuota"], in: quota),
monthlyNextRefreshTime: self.anyDate(
for: ["perBillMonthQuotaNextRefreshTime", "perMonthQuotaNextRefreshTime"],
in: quota),
updatedAt: now)
if snapshot.fiveHourTotalQuota == nil,
snapshot.weeklyTotalQuota == nil,
snapshot.monthlyTotalQuota == nil
{
if let fallback = self.parsePlanVisibleActiveFallback(
payload: dictionary,
instanceInfo: instanceInfo,
now: now)
{
return fallback
}
let diagnostics = self.payloadDiagnostics(payload: dictionary)
Self.log.error("Alibaba coding plan payload had no usable windows: \(diagnostics)")
throw AlibabaCodingPlanUsageError.parseFailed("No quota windows found in payload (\(diagnostics))")
}
return snapshot
}
private static func findPlanName(in payload: [String: Any]) -> String? {
if let infos = self.findFirstArray(
forKeys: ["codingPlanInstanceInfos", "coding_plan_instance_infos"],
in: payload)
{
for item in infos {
guard let info = item as? [String: Any] else { continue }
let candidates = [
self.anyString(for: ["planName", "plan_name"], in: info),
self.anyString(for: ["instanceName", "instance_name"], in: info),
self.anyString(for: ["packageName", "package_name"], in: info),
]
for candidate in candidates {
if let candidate, !candidate.isEmpty {
return candidate
}
}
}
}
return self.findFirstString(forKeys: ["planName", "plan_name", "packageName", "package_name"], in: payload)
}
private static func findActiveInstanceInfo(in payload: [String: Any], now: Date = Date()) -> [String: Any]? {
guard let infos = self.findFirstArray(
forKeys: ["codingPlanInstanceInfos", "coding_plan_instance_infos"],
in: payload)
else {
return nil
}
var first: [String: Any]?
var best: [String: Any]?
var bestScore = Int.min
for item in infos {
guard let info = item as? [String: Any] else { continue }
first = first ?? info
let score = self.activeSignalScore(in: info, now: now)
if score > bestScore {
best = info
bestScore = score
}
}
if bestScore > 0 {
return best
}
return first
}
private static func parsePlanVisibleActiveFallback(
payload: [String: Any],
instanceInfo: [String: Any]?,
now: Date) -> AlibabaCodingPlanUsageSnapshot?
{
let source = instanceInfo ?? payload
guard self.hasPositiveActivePlanSignal(in: source, payload: payload, now: now),
let planName = self.findPlanName(in: source) ?? self.findPlanName(in: payload)
else {
return nil
}
return AlibabaCodingPlanUsageSnapshot(
planName: planName,
fiveHourUsedQuota: nil,
fiveHourTotalQuota: nil,
fiveHourNextRefreshTime: nil,
weeklyUsedQuota: nil,
weeklyTotalQuota: nil,
weeklyNextRefreshTime: nil,
monthlyUsedQuota: nil,
monthlyTotalQuota: nil,
monthlyNextRefreshTime: nil,
updatedAt: now)
}
private static func hasPositiveActivePlanSignal(
in source: [String: Any],
payload: [String: Any],
now: Date) -> Bool
{
if self.containsPlanInstances(in: payload) {
return self.activeSignalScore(in: source, now: now) > 0
}
return self.activeSignalScore(in: source, now: now) > 0 || self.activeSignalScore(in: payload, now: now) > 0
}
private static func containsPlanInstances(in payload: [String: Any]) -> Bool {
guard let infos = self.findFirstArray(
forKeys: ["codingPlanInstanceInfos", "coding_plan_instance_infos"],
in: payload)
else {
return false
}
return infos.contains { $0 is [String: Any] }
}
private static func activeSignalScore(in source: [String: Any], now: Date) -> Int {
if let status = self.anyString(for: ["status", "instanceStatus"], in: source)?.uppercased() {
if ["VALID", "ACTIVE"].contains(status) {
return 3
}
if ["EXPIRED", "INVALID", "INACTIVE", "DISABLED", "TERMINATED", "STOPPED"].contains(status) {
return -1
}
}
if let isActive = self.anyBool(for: ["isActive", "active"], in: source) {
return isActive ? 3 : -1
}
if let expiry = self.anyDate(for: ["endTime", "periodEndTime", "expireTime", "expirationTime"], in: source),
expiry.timeIntervalSince(now) > 0
{
return 1
}
return 0
}
private static func payloadDiagnostics(payload: [String: Any]) -> String {
let topKeys = payload.keys.sorted()
let dataDict = self.findFirstDictionary(forKeys: ["data", "successResponse", "success_response"], in: payload)
let dataKeys = dataDict?.keys.sorted() ?? []
let instanceInfo = self.findActiveInstanceInfo(in: payload)
let instanceKeys = instanceInfo?.keys.sorted() ?? []
let hasQuota = self.findQuotaInfo(in: payload) != nil
let planUsage =
self.anyString(for: ["planUsage", "usageRate", "usage", "usageValue"], in: instanceInfo ?? [:]) ??
self.findFirstString(forKeys: ["planUsage", "usageRate", "usage", "usageValue"], in: payload)
let compactPlanUsage = planUsage?.replacingOccurrences(of: "\n", with: " ") ?? "none"
let status = self.anyString(for: ["status", "instanceStatus"], in: instanceInfo ?? [:]) ?? "none"
return "topKeys=\(topKeys.joined(separator: ",")) dataKeys=\(dataKeys.joined(separator: ",")) " +
"instanceKeys=\(instanceKeys.joined(separator: ",")) hasQuota=\(hasQuota ? "1" : "0") " +
"status=\(status) planUsage=\(compactPlanUsage)"
}
private static func findQuotaInfo(in payload: [String: Any]) -> [String: Any]? {
if let direct = self.findFirstDictionary(
forKeys: ["codingPlanQuotaInfo", "coding_plan_quota_info"],
in: payload)
{
return direct
}
return self.findFirstDictionary(
matchingAnyKey: [
"per5HourUsedQuota",
"per5HourTotalQuota",
"perWeekUsedQuota",
"perWeekTotalQuota",
"perBillMonthUsedQuota",
"perBillMonthTotalQuota",
],
in: payload)
}
private static func findFirstDictionary(forKeys keys: [String], in value: Any) -> [String: Any]? {
guard let dict = value as? [String: Any] else { return nil }
for key in keys {
if let nested = dict[key] as? [String: Any] {
return nested
}
}
for nestedValue in dict.values {
if let nested = self.findFirstDictionary(forKeys: keys, in: nestedValue) {
return nested
}
}
if let array = value as? [Any] {
for item in array {
if let nested = self.findFirstDictionary(forKeys: keys, in: item) {
return nested
}
}
}
return nil
}
private static func findFirstDictionary(matchingAnyKey keys: [String], in value: Any) -> [String: Any]? {
if let dict = value as? [String: Any] {
if keys.contains(where: { dict[$0] != nil }) {
return dict
}
for nestedValue in dict.values {
if let nested = self.findFirstDictionary(matchingAnyKey: keys, in: nestedValue) {
return nested
}
}
return nil
}
if let array = value as? [Any] {
for item in array {
if let nested = self.findFirstDictionary(matchingAnyKey: keys, in: item) {
return nested
}
}
}
return nil
}
private static func findFirstArray(forKeys keys: [String], in value: Any) -> [Any]? {
guard let dict = value as? [String: Any] else {
if let array = value as? [Any] {
for item in array {
if let found = self.findFirstArray(forKeys: keys, in: item) {
return found
}
}
}
return nil
}
for key in keys {
if let array = dict[key] as? [Any] {
return array
}
}
for nested in dict.values {
if let found = self.findFirstArray(forKeys: keys, in: nested) {
return found
}
}
return nil
}
private static func findFirstInt(forKeys keys: [String], in value: Any) -> Int? {
if let dict = value as? [String: Any] {
for key in keys {
if let parsed = self.parseInt(dict[key]) {
return parsed
}
}
for nested in dict.values {
if let parsed = self.findFirstInt(forKeys: keys, in: nested) {
return parsed
}
}
return nil
}
if let array = value as? [Any] {
for item in array {
if let parsed = self.findFirstInt(forKeys: keys, in: item) {
return parsed
}
}
}
return nil
}
private static func findFirstString(forKeys keys: [String], in value: Any) -> String? {
if let dict = value as? [String: Any] {
for key in keys {
if let parsed = self.parseString(dict[key]) {
return parsed
}
}
for nested in dict.values {
if let parsed = self.findFirstString(forKeys: keys, in: nested) {
return parsed
}
}
return nil
}
if let array = value as? [Any] {
for item in array {
if let parsed = self.findFirstString(forKeys: keys, in: item) {
return parsed
}
}
}
return nil
}
private static func findFirstDate(forKeys keys: [String], in value: Any) -> Date? {
if let dict = value as? [String: Any] {
for key in keys {
if let parsed = self.parseDate(dict[key]) {
return parsed
}
}
for nested in dict.values {
if let parsed = self.findFirstDate(forKeys: keys, in: nested) {
return parsed
}
}
return nil
}
if let array = value as? [Any] {
for item in array {
if let parsed = self.findFirstDate(forKeys: keys, in: item) {
return parsed
}
}
}
return nil
}
private static func expandedJSON(_ value: Any) -> Any {
if let dict = value as? [String: Any] {
var expanded: [String: Any] = [:]
expanded.reserveCapacity(dict.count)
for (key, nested) in dict {
expanded[key] = self.expandedJSON(nested)
}
return expanded
}
if let array = value as? [Any] {
return array.map { self.expandedJSON($0) }
}
if let string = value as? String,
let data = string.data(using: .utf8),
let nested = try? JSONSerialization.jsonObject(with: data, options: []),
nested is [String: Any] || nested is [Any]
{
return self.expandedJSON(nested)
}
return value
}
private static func anyInt(for keys: [String], in dict: [String: Any]) -> Int? {
for key in keys {
if let value = self.parseInt(dict[key]) {
return value
}
}
return nil
}
private static func anyString(for keys: [String], in dict: [String: Any]) -> String? {
for key in keys {
if let value = self.parseString(dict[key]) {
return value
}
}
return nil
}
private static func anyDate(for keys: [String], in dict: [String: Any]) -> Date? {
for key in keys {
if let value = self.parseDate(dict[key]) {
return value
}
}
return nil
}
private static func anyPercent(for keys: [String], in dict: [String: Any]) -> Double? {
for key in keys {
if let value = self.parsePercent(dict[key]) {
return value
}
}
return nil
}
private static func anyBool(for keys: [String], in dict: [String: Any]) -> Bool? {
for key in keys {
if let value = self.parseBool(dict[key]) {
return value
}
}
return nil
}
private static func findFirstPercent(forKeys keys: [String], in value: Any) -> Double? {
if let dict = value as? [String: Any] {
for key in keys {
if let parsed = self.parsePercent(dict[key]) {
return parsed
}
}
for nested in dict.values {
if let parsed = self.findFirstPercent(forKeys: keys, in: nested) {
return parsed
}
}
return nil
}
if let array = value as? [Any] {
for item in array {
if let parsed = self.findFirstPercent(forKeys: keys, in: item) {
return parsed
}
}
}
return nil
}
private static func parseDate(_ raw: Any?) -> Date? {
if let intValue = self.parseInt(raw) {
if intValue > 1_000_000_000_000 {
return Date(timeIntervalSince1970: TimeInterval(intValue) / 1000)
}
if intValue > 1_000_000_000 {
return Date(timeIntervalSince1970: TimeInterval(intValue))
}
}
if let string = self.parseString(raw) {
let formatter = ISO8601DateFormatter()
if let date = formatter.date(from: string) {
return date
}
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
for format in ["yyyy-MM-dd HH:mm", "yyyy-MM-dd HH:mm:ss"] {
dateFormatter.dateFormat = format
if let date = dateFormatter.date(from: string) {
return date
}
}
}
return nil
}
private static func parseInt(_ raw: Any?) -> Int? {
if let value = raw as? Int { return value }
if let value = raw as? Int64 { return Int(value) }
if let value = raw as? Double { return Int(value) }
if let value = raw as? NSNumber { return value.intValue }
if let value = raw as? String {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
return Int(trimmed)
}
return nil
}
private static func parseString(_ raw: Any?) -> String? {
guard let value = raw as? String else { return nil }
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
private static func parsePercent(_ raw: Any?) -> Double? {
if let intValue = self.parseInt(raw) {
return max(0, min(Double(intValue), 100))
}
guard let rawString = self.parseString(raw) else { return nil }
let cleaned = rawString
.replacingOccurrences(of: "%", with: "")
.trimmingCharacters(in: .whitespacesAndNewlines)
guard let parsed = Double(cleaned) else { return nil }
return max(0, min(parsed, 100))
}
private static func parseBool(_ raw: Any?) -> Bool? {
if let value = raw as? Bool {
return value
}
if let number = raw as? NSNumber {
return number.boolValue
}
guard let string = self.parseString(raw)?.lowercased() else { return nil }
switch string {
case "true", "1", "yes", "active", "valid":
return true
case "false", "0", "no", "inactive", "invalid", "expired":
return false
default:
return nil
}
}
private static func matchFirstGroup(pattern: String, in text: String) -> String? {
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
let range = NSRange(text.startIndex..<text.endIndex, in: text)
guard let match = regex.firstMatch(in: text, options: [], range: range),
match.numberOfRanges > 1,
let valueRange = Range(match.range(at: 1), in: text)
else {
return nil
}
let value = text[valueRange].trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : String(value)
}
private static func extractConsoleSECToken(from html: String) -> String? {
let patterns = [
#"SEC_TOKEN\s*:\s*\"([^\"]+)\""#,
#"SEC_TOKEN\s*:\s*'([^']+)'"#,
#"secToken\s*:\s*\"([^\"]+)\""#,
#"sec_token\s*:\s*\"([^\"]+)\""#,
#"sec_token\s*:\s*'([^']+)'"#,
#"\"SEC_TOKEN\"\s*:\s*\"([^\"]+)\""#,
#"\"sec_token\"\s*:\s*\"([^\"]+)\""#,
]
for pattern in patterns {
if let token = self.matchFirstGroup(pattern: pattern, in: html), !token.isEmpty {
return token
}
}
return nil
}
private static func extractCookieValue(name: String, from cookieHeader: String) -> String? {
let segments = cookieHeader.split(separator: ";")
for segment in segments {
let part = segment.trimmingCharacters(in: .whitespacesAndNewlines)
guard let index = part.firstIndex(of: "=") else { continue }
let key = String(part[..<index]).trimmingCharacters(in: .whitespacesAndNewlines)
if key == name {
let value = String(part[part.index(after: index)...]).trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
}
}
return nil
}
}
public enum AlibabaCodingPlanUsageError: LocalizedError, Sendable, Equatable {
case loginRequired
case invalidCredentials
case networkError(String)
case apiError(String)
case parseFailed(String)
case apiKeyUnavailableInRegion
var shouldRetryOnAlternateRegion: Bool {
switch self {
case .loginRequired:
true
case .invalidCredentials:
true
case .apiKeyUnavailableInRegion:
false
case let .apiError(message):
message.contains("HTTP 404") || message.contains("HTTP 403")
case let .parseFailed(message):
message.contains("Missing coding plan quota data") || message.contains("No quota windows found")
case .networkError:
false
}
}
public var errorDescription: String? {
switch self {
case .loginRequired:
"Alibaba Coding Plan console login is required. " +
"Sign in to Model Studio/Bailian in a supported browser or paste a Cookie header."
case .invalidCredentials:
"Alibaba Coding Plan API credentials are invalid or expired."
case .apiKeyUnavailableInRegion:
"Alibaba Coding Plan quota is not available through Coding Plan API keys for this account/region. " +
"Use cookie authentication in Settings -> Providers -> Alibaba if the Alibaba console exposes a " +
"compatible session, or switch regions if quota API access is available there."
case let .networkError(message):
"Alibaba Coding Plan network error: \(message)"
case let .apiError(message):
"Alibaba Coding Plan API error: \(message)"
case let .parseFailed(message):
"Failed to parse Alibaba Coding Plan response: \(message)"
}
}
}
// swiftlint:enable type_body_length