Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ body:
- Greptile
- Google Gemini
- Antigravity
- Muse Code
- Not provider-specific
- Other

Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ building, testing, or releasing the app.
to full use at reset. Fresh windows and missing quota show n/a.
([#518](https://github.com/hemsoft-dev/codexbar-ios/issues/518))

- Add Account now explains that Muse Code isn't available on iPhone. Meta
doesn't offer a sign-in or usage service that iPhone apps can use for Muse
Code subscriptions, and CodexBar won't estimate an allowance from token
activity. ([#522](https://github.com/hemsoft-dev/codexbar-ios/issues/522))

### Fixed

- Keep missing Grok and X Subscription usage unavailable instead of inferring
Expand All @@ -23,6 +28,13 @@ building, testing, or releasing the app.

### Developer Experience

- Add a Muse Code allowance reader for schema 1 of `muse usage --json`, with
sanitized fixtures and distinct unavailable states, for use if Meta ships a
supported iPhone source. Muse Code can't be added, refreshed or published,
and gets no Sub Utilization Score. The contract and its limits are in
[MUSE-CODE.md](MUSE-CODE.md).
([#522](https://github.com/hemsoft-dev/codexbar-ios/issues/522))

- Include both subscription-utilization journeys in the release manifest so
manual iOS validation can start and verify all 53 journeys per device family.
Retain the last verified-renewal predicate state if native expiration
Expand Down
12 changes: 12 additions & 0 deletions CodexBarIOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
A46700000000000000043001 /* WatchAccessReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A46700000000000000040004 /* WatchAccessReaderTests.swift */; };
A45100000000000000000201 /* UITestOpenCodeRenewalProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45100000000000000000202 /* UITestOpenCodeRenewalProvider.swift */; };
A52200000000000000000105 /* UITestMuseCodeFixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52200000000000000000106 /* UITestMuseCodeFixture.swift */; };
A45100000000000000000101 /* OpenCodeSubscriptionBilling.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45100000000000000000102 /* OpenCodeSubscriptionBilling.swift */; };
A44600000000000000000601 /* UITestSubscriptionBillingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A44600000000000000000602 /* UITestSubscriptionBillingProtocol.swift */; };
A44600000000000000000501 /* SubscriptionBillingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A44600000000000000000502 /* SubscriptionBillingTests.swift */; };
Expand Down Expand Up @@ -68,6 +69,7 @@
10000000000000000000004C /* ProviderParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000004C /* ProviderParsingTests.swift */; };
A29600000000000000000003 /* GeminiUsageProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A29600000000000000000004 /* GeminiUsageProviderTests.swift */; };
A31400000000000000000401 /* AntigravityUsageProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31400000000000000000402 /* AntigravityUsageProviderTests.swift */; };
A52200000000000000000107 /* MuseCodeAllowanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52200000000000000000108 /* MuseCodeAllowanceTests.swift */; };
A29900000000000000000201 /* GeminiBrowserSignInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A29900000000000000000202 /* GeminiBrowserSignInTests.swift */; };
10000000000000000000004D /* UsageHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000004D /* UsageHistoryTests.swift */; };
10000000000000000000004E /* ProviderNetworkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000004E /* ProviderNetworkTests.swift */; };
Expand Down Expand Up @@ -112,6 +114,7 @@
A29600000000000000000001 /* GeminiUsageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A29600000000000000000002 /* GeminiUsageProvider.swift */; };
A31400000000000000000301 /* AntigravityUsageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31400000000000000000302 /* AntigravityUsageProvider.swift */; };
A31400000000000000000201 /* AntigravityQuotaParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31400000000000000000202 /* AntigravityQuotaParser.swift */; };
A52200000000000000000101 /* MuseCodeAllowance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52200000000000000000102 /* MuseCodeAllowance.swift */; };
A31400000000000000000101 /* AntigravityCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31400000000000000000102 /* AntigravityCredentials.swift */; };
A35300000000000000000301 /* OpenCodeDeviceAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A35300000000000000000302 /* OpenCodeDeviceAuthService.swift */; };
A35300000000000000000401 /* OpenCodeConsoleUsageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A35300000000000000000402 /* OpenCodeConsoleUsageProvider.swift */; };
Expand Down Expand Up @@ -379,6 +382,7 @@
/* Begin PBXFileReference section */
A46700000000000000040004 /* WatchAccessReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchAccessReaderTests.swift; sourceTree = "<group>"; };
A45100000000000000000202 /* UITestOpenCodeRenewalProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestOpenCodeRenewalProvider.swift; sourceTree = "<group>"; };
A52200000000000000000106 /* UITestMuseCodeFixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestMuseCodeFixture.swift; sourceTree = "<group>"; };
A45100000000000000000102 /* OpenCodeSubscriptionBilling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCodeSubscriptionBilling.swift; sourceTree = "<group>"; };
A44600000000000000000602 /* UITestSubscriptionBillingProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestSubscriptionBillingProtocol.swift; sourceTree = "<group>"; };
A44600000000000000000502 /* SubscriptionBillingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodexBarIOSTests/SubscriptionBillingTests.swift; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -431,6 +435,7 @@
20000000000000000000004C /* ProviderParsingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderParsingTests.swift; sourceTree = "<group>"; };
A29600000000000000000004 /* GeminiUsageProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiUsageProviderTests.swift; sourceTree = "<group>"; };
A31400000000000000000402 /* AntigravityUsageProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AntigravityUsageProviderTests.swift; sourceTree = "<group>"; };
A52200000000000000000108 /* MuseCodeAllowanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuseCodeAllowanceTests.swift; sourceTree = "<group>"; };
A29900000000000000000202 /* GeminiBrowserSignInTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiBrowserSignInTests.swift; sourceTree = "<group>"; };
20000000000000000000004D /* UsageHistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsageHistoryTests.swift; sourceTree = "<group>"; };
20000000000000000000004E /* ProviderNetworkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderNetworkTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -470,6 +475,7 @@
A29600000000000000000002 /* GeminiUsageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiUsageProvider.swift; sourceTree = "<group>"; };
A31400000000000000000302 /* AntigravityUsageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AntigravityUsageProvider.swift; sourceTree = "<group>"; };
A31400000000000000000202 /* AntigravityQuotaParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AntigravityQuotaParser.swift; sourceTree = "<group>"; };
A52200000000000000000102 /* MuseCodeAllowance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuseCodeAllowance.swift; sourceTree = "<group>"; };
A31400000000000000000102 /* AntigravityCredentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AntigravityCredentials.swift; sourceTree = "<group>"; };
A35300000000000000000302 /* OpenCodeDeviceAuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCodeDeviceAuthService.swift; sourceTree = "<group>"; };
A35300000000000000000402 /* OpenCodeConsoleUsageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCodeConsoleUsageProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -828,6 +834,7 @@
A29600000000000000000002 /* GeminiUsageProvider.swift */,
A31400000000000000000302 /* AntigravityUsageProvider.swift */,
A31400000000000000000202 /* AntigravityQuotaParser.swift */,
A52200000000000000000102 /* MuseCodeAllowance.swift */,
A31400000000000000000102 /* AntigravityCredentials.swift */,
A29900000000000000000002 /* GeminiBrowserSessionPolicy.swift */,
A40600000000000000000202 /* GreptileDashboardClient.swift */,
Expand All @@ -838,6 +845,7 @@
A35300000000000000000402 /* OpenCodeConsoleUsageProvider.swift */,
A45100000000000000000102 /* OpenCodeSubscriptionBilling.swift */,
A45100000000000000000202 /* UITestOpenCodeRenewalProvider.swift */,
A52200000000000000000106 /* UITestMuseCodeFixture.swift */,
A35500000000000000000102 /* GrokDeviceAuthService.swift */,
A35500000000000000000202 /* GrokUsageProvider.swift */,
A45800000000000000000102 /* XSubscriptionUsageParser.swift */,
Expand Down Expand Up @@ -938,6 +946,7 @@
20000000000000000000004C /* ProviderParsingTests.swift */,
A29600000000000000000004 /* GeminiUsageProviderTests.swift */,
A31400000000000000000402 /* AntigravityUsageProviderTests.swift */,
A52200000000000000000108 /* MuseCodeAllowanceTests.swift */,
A29900000000000000000202 /* GeminiBrowserSignInTests.swift */,
200000000000000000000050 /* TestSupport.swift */,
A42000000000000000000502 /* ClaudeUsageResetInventoryTests.swift */,
Expand Down Expand Up @@ -1453,6 +1462,7 @@
A29600000000000000000001 /* GeminiUsageProvider.swift in Sources */,
A31400000000000000000301 /* AntigravityUsageProvider.swift in Sources */,
A31400000000000000000201 /* AntigravityQuotaParser.swift in Sources */,
A52200000000000000000101 /* MuseCodeAllowance.swift in Sources */,
A31400000000000000000101 /* AntigravityCredentials.swift in Sources */,
A29900000000000000000001 /* GeminiBrowserSessionPolicy.swift in Sources */,
A37400000000000000000001 /* GeminiBrowserInspectionState.swift in Sources */,
Expand All @@ -1461,6 +1471,7 @@
A35300000000000000000401 /* OpenCodeConsoleUsageProvider.swift in Sources */,
A45100000000000000000101 /* OpenCodeSubscriptionBilling.swift in Sources */,
A45100000000000000000201 /* UITestOpenCodeRenewalProvider.swift in Sources */,
A52200000000000000000105 /* UITestMuseCodeFixture.swift in Sources */,
A35500000000000000000101 /* GrokDeviceAuthService.swift in Sources */,
A35500000000000000000201 /* GrokUsageProvider.swift in Sources */,
A45800000000000000000101 /* XSubscriptionUsageParser.swift in Sources */,
Expand Down Expand Up @@ -1545,6 +1556,7 @@
10000000000000000000004C /* ProviderParsingTests.swift in Sources */,
A29600000000000000000003 /* GeminiUsageProviderTests.swift in Sources */,
A31400000000000000000401 /* AntigravityUsageProviderTests.swift in Sources */,
A52200000000000000000107 /* MuseCodeAllowanceTests.swift in Sources */,
A29900000000000000000201 /* GeminiBrowserSignInTests.swift in Sources */,
100000000000000000000050 /* TestSupport.swift in Sources */,
A42000000000000000000501 /* ClaudeUsageResetInventoryTests.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions CodexBarIOS/Models/GoogleUsageMetricCatalog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public enum GoogleUsageMetricCatalog {
codingDefinitions
case .codex:
[Definition(sourceProviderID: .codex, key: CodexUsageParser.creditsPoolStableKey, label: "Credits pool", window: "balance")]
case .museCode:
MuseCodeAllowance.Window.allCases.map {
Definition(sourceProviderID: .museCode, key: $0.rawValue, label: $0.label, window: $0 == .window ? "5h" : "weekly")
}
default:
[]
}
Expand Down
2 changes: 1 addition & 1 deletion CodexBarIOS/Models/ProviderAccountConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ public extension ProviderAccountConfiguration {
ProviderAccountConfiguration(providerID: providerID, authMethod: .apiKey)
case .cursor:
ProviderAccountConfiguration(providerID: providerID, authMethod: .browserSession)
case .greptile:
case .greptile, .museCode:
ProviderAccountConfiguration(providerID: providerID, authMethod: .browserSession)
}
}
Expand Down
15 changes: 14 additions & 1 deletion CodexBarIOS/Models/ProviderID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum ProviderID: String, CaseIterable, Codable, Identifiable, Sendable {
case greptile
case gemini
case antigravity
case museCode

public var id: String {
rawValue
Expand Down Expand Up @@ -47,18 +48,30 @@ public enum ProviderID: String, CaseIterable, Codable, Identifiable, Sendable {
"Google Gemini"
case .antigravity:
"Antigravity"
case .museCode:
"Muse Code"
}
}

public var supportsPlanBadge: Bool {
switch self {
case .codex, .copilot, .githubBilling, .claude, .grok, .xSubscription:
case .codex, .copilot, .githubBilling, .claude, .grok, .xSubscription, .museCode:
true
case .openRouter, .openCodeZen, .moonshot, .cursor, .greptile, .gemini, .antigravity:
false
}
}

/// Why this provider can't be connected on iPhone, or nil when it has a supported sign-in.
/// Add Account names it with this reason; it can't be added, refreshed or hold an account slot (MUSE-CODE.md).
public var phoneUnavailableReason: String? {
self == .museCode ? MuseCodeAllowance.phoneAcquisitionUnavailableReason : nil
}

public var isAvailableOnIPhone: Bool {
phoneUnavailableReason == nil
}

/// Providers signed in through xAI's Grok CLI device authorization. They share the credential
/// format, Keychain lock, subject verification and generated-label rules, but not billing reads.
public var usesXAIConsumerAuth: Bool {
Expand Down
13 changes: 12 additions & 1 deletion CodexBarIOS/Models/SubscriptionUtilizationScore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public struct SubscriptionUtilizationScore: Equatable, Sendable {
return duration.isFinite && abs(duration - 7 * 86_400) <= 60
}

public static func applies(to bar: UsageBar, providerID: ProviderID) -> Bool {
providerID.hasSubscriptionUtilizationScore && applies(to: bar)
}

public static func applies(to bar: UsageBar) -> Bool {
if let start = bar.projectionPeriodStart, let end = bar.projectionPeriodEnd {
return isWeekly(start: start, end: end)
Expand Down Expand Up @@ -68,7 +72,7 @@ public enum SubscriptionUtilizationFleet {

/// Average weekly pools once per account, then apply its subscription price once.
public static func entry(for result: ProviderUsageResult, at now: Date = Date()) -> Entry? {
guard result.hasCurrentBars else { return nil }
guard result.hasCurrentBars, result.providerID.hasSubscriptionUtilizationScore else { return nil }
let scores = result.enabledBarIndices.compactMap { SubscriptionUtilizationScore(bar: result.bars[$0], at: now)?.value }
guard !scores.isEmpty else { return nil }
let mean = Double(scores.reduce(0, +)) / Double(scores.count)
Expand All @@ -84,3 +88,10 @@ public enum SubscriptionUtilizationFleet {
return SubscriptionUtilizationScore(value: Int(weighted.rounded(.toNearestOrEven)))
}
}

public extension ProviderID {
/// Muse Code has no verified price or scoring scope, so its weekly window never gets a score (MUSE-CODE.md).
var hasSubscriptionUtilizationScore: Bool {
self != .museCode
}
}
8 changes: 5 additions & 3 deletions CodexBarIOS/Services/AccountPlanAccess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,17 @@ public struct AccountPlanAccess: Equatable, Sendable {
}

public func permitsRefresh(_ configuration: ProviderAccountConfiguration) -> Bool {
guard configuration.isEnabled, configuration.providerID != .antigravity else { return false }
guard configuration.isEnabled, configuration.providerID != .antigravity,
configuration.providerID.isAvailableOnIPhone else { return false }
return hasProAccess || (isResolved && activeAccountID == configuration.id)
}

public func permitsInsertion(
for providerID: ProviderID,
configurations: [ProviderAccountConfiguration]
) -> Bool {
providerID == .antigravity || hasProAccess
|| !configurations.contains { $0.isEnabled && $0.providerID != .antigravity }
guard providerID.isAvailableOnIPhone else { return false }
return providerID == .antigravity || hasProAccess
|| !configurations.contains { $0.isEnabled && $0.providerID != .antigravity && $0.providerID.isAvailableOnIPhone }
}
}
2 changes: 2 additions & 0 deletions CodexBarIOS/Services/FeedbackSupportDestination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ extension ProviderID {
"Google Gemini"
case .antigravity:
"Antigravity"
case .museCode:
"Muse Code"
}
}
}
Expand Down
Loading
Loading