Skip to content

Commit 3333f62

Browse files
committed
feat(mimo): add MiMo provider (port from upstream PR steipete#651)
- Add MiMoProviderDescriptor, cookie-based usage fetcher, settings - Register .mimo in UsageProvider/IconStyle enums, descriptor registry, implementation registry, settings snapshot, CLI/widget/test switches - Add MiMoProviderSettings with cookieSource + manualCookieHeader Based on steipete#651 (mimo-only hunks, no alibaba/perplexity).
1 parent ace7ab8 commit 3333f62

19 files changed

Lines changed: 1659 additions & 2 deletions
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import AppKit
2+
import CodexBarCore
3+
import CodexBarMacroSupport
4+
import Foundation
5+
import SwiftUI
6+
7+
@ProviderImplementationRegistration
8+
struct MiMoProviderImplementation: ProviderImplementation {
9+
let id: UsageProvider = .mimo
10+
let supportsLoginFlow: Bool = true
11+
12+
@MainActor
13+
func presentation(context _: ProviderPresentationContext) -> ProviderPresentation {
14+
ProviderPresentation { _ in "web" }
15+
}
16+
17+
@MainActor
18+
func observeSettings(_ settings: SettingsStore) {
19+
_ = settings.miMoCookieSource
20+
_ = settings.miMoCookieHeader
21+
}
22+
23+
@MainActor
24+
func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? {
25+
.mimo(context.settings.miMoSettingsSnapshot(tokenOverride: context.tokenOverride))
26+
}
27+
28+
@MainActor
29+
func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] {
30+
let cookieBinding = Binding(
31+
get: { context.settings.miMoCookieSource.rawValue },
32+
set: { raw in
33+
context.settings.miMoCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto
34+
})
35+
let cookieOptions = ProviderCookieSourceUI.options(
36+
allowsOff: false,
37+
keychainDisabled: context.settings.debugDisableKeychainAccess)
38+
let cookieSubtitle: () -> String? = {
39+
ProviderCookieSourceUI.subtitle(
40+
source: context.settings.miMoCookieSource,
41+
keychainDisabled: context.settings.debugDisableKeychainAccess,
42+
auto: "Automatic imports Chrome browser cookies from Xiaomi MiMo.",
43+
manual: "Paste a Cookie header from platform.xiaomimimo.com.",
44+
off: "Xiaomi MiMo cookies are disabled.")
45+
}
46+
47+
return [
48+
ProviderSettingsPickerDescriptor(
49+
id: "mimo-cookie-source",
50+
title: "Cookie source",
51+
subtitle: "Automatic imports Chrome browser cookies from Xiaomi MiMo.",
52+
dynamicSubtitle: cookieSubtitle,
53+
binding: cookieBinding,
54+
options: cookieOptions,
55+
isVisible: nil,
56+
onChange: nil,
57+
trailingText: {
58+
guard let entry = CookieHeaderCache.load(provider: .mimo) else { return nil }
59+
let when = entry.storedAt.relativeDescription()
60+
return "Cached: \(entry.sourceLabel)\(when)"
61+
}),
62+
]
63+
}
64+
65+
@MainActor
66+
func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
67+
[
68+
ProviderSettingsFieldDescriptor(
69+
id: "mimo-cookie",
70+
title: "",
71+
subtitle: "",
72+
kind: .secure,
73+
placeholder: "Cookie: ...",
74+
binding: context.stringBinding(\.miMoCookieHeader),
75+
actions: [
76+
ProviderSettingsActionDescriptor(
77+
id: "mimo-open-balance",
78+
title: "Open MiMo Balance",
79+
style: .link,
80+
isVisible: nil,
81+
perform: {
82+
guard let url = URL(string: "https://platform.xiaomimimo.com/#/console/balance") else {
83+
return
84+
}
85+
NSWorkspace.shared.open(url)
86+
}),
87+
],
88+
isVisible: { context.settings.miMoCookieSource == .manual },
89+
onActivate: { context.settings.ensureMiMoCookieLoaded() }),
90+
]
91+
}
92+
93+
@MainActor
94+
func runLoginFlow(context _: ProviderLoginContext) async -> Bool {
95+
let loginURL = "https://platform.xiaomimimo.com/api/v1/genLoginUrl?currentPath=%2F%23%2Fconsole%2Fbalance"
96+
guard let url = URL(string: loginURL) else {
97+
return false
98+
}
99+
NSWorkspace.shared.open(url)
100+
return false
101+
}
102+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import CodexBarCore
2+
import Foundation
3+
4+
extension SettingsStore {
5+
var miMoCookieHeader: String {
6+
get { self.configSnapshot.providerConfig(for: .mimo)?.sanitizedCookieHeader ?? "" }
7+
set {
8+
self.updateProviderConfig(provider: .mimo) { entry in
9+
entry.cookieHeader = self.normalizedConfigValue(newValue)
10+
}
11+
self.logSecretUpdate(provider: .mimo, field: "cookieHeader", value: newValue)
12+
}
13+
}
14+
15+
var miMoCookieSource: ProviderCookieSource {
16+
get { self.resolvedCookieSource(provider: .mimo, fallback: .auto) }
17+
set {
18+
self.updateProviderConfig(provider: .mimo) { entry in
19+
entry.cookieSource = newValue
20+
}
21+
self.logProviderModeChange(provider: .mimo, field: "cookieSource", value: newValue.rawValue)
22+
}
23+
}
24+
25+
func ensureMiMoCookieLoaded() {}
26+
}
27+
28+
extension SettingsStore {
29+
func miMoSettingsSnapshot(tokenOverride: TokenAccountOverride?) -> ProviderSettingsSnapshot.MiMoProviderSettings {
30+
_ = tokenOverride
31+
return ProviderSettingsSnapshot.MiMoProviderSettings(
32+
cookieSource: self.miMoCookieSource,
33+
manualCookieHeader: self.miMoCookieHeader)
34+
}
35+
}

Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ enum ProviderImplementationRegistry {
4141
case .aigocode: AigoCodeProviderImplementation()
4242
case .trae: TraeProviderImplementation()
4343
case .stepfun: StepFunProviderImplementation()
44+
case .mimo: MiMoProviderImplementation()
4445
}
4546
}
4647

Lines changed: 4 additions & 0 deletions
Loading

Sources/CodexBar/UsageStore.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,8 @@ extension UsageStore {
12641264
text = "Trae: local probe (no API key needed)"
12651265
case .stepfun:
12661266
text = "StepFun: local probe (no API key needed)"
1267+
case .mimo:
1268+
text = "MiMo: local probe (cookie-based)"
12671269
case .gemini, .antigravity, .opencode, .factory, .copilot, .vertexai, .kilo, .kiro, .kimi, .kimik2,
12681270
.jetbrains:
12691271
text = unimplementedDebugLogMessages[provider] ?? "Debug log not yet implemented"

Sources/CodexBarCLI/TokenAccountCLI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ struct TokenAccountCLIContext {
158158
jetbrains: ProviderSettingsSnapshot.JetBrainsProviderSettings(
159159
ideBasePath: nil))
160160
case .gemini, .antigravity, .copilot, .kiro, .vertexai, .kimik2, .synthetic, .openrouter, .warp,
161-
.qwen, .doubao, .zenmux, .aigocode, .trae, .stepfun:
161+
.qwen, .doubao, .zenmux, .aigocode, .trae, .stepfun, .mimo:
162162
return nil
163163
}
164164
}

0 commit comments

Comments
 (0)