mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-19 07:43:09 +00:00
Add OpenClaw, Roo Code, and KiloCode providers (#175)
- OpenClaw: JSONL parser with multi-path discovery, tool extraction (toolCall + tool_use block types), model tracking via model_change and custom model-snapshot events - Roo Code + KiloCode: shared Cline-family parser extracts model from <model> tags in api_conversation_history.json, strips provider prefixes from model names - Add cline-auto and openclaw-auto aliases and display names - Add menubar provider filters and tab colors for all three - Show cached data instantly instead of blocking on CLI refresh
This commit is contained in:
parent
ce78ac52c1
commit
ec2de6a642
13 changed files with 1034 additions and 7 deletions
|
|
@ -65,13 +65,13 @@ final class AppStore {
|
|||
/// Switch to a period. Always fetches fresh data so the user never sees stale numbers.
|
||||
func switchTo(period: Period) async {
|
||||
selectedPeriod = period
|
||||
await refresh(includeOptimize: true)
|
||||
await refresh(includeOptimize: true, force: true)
|
||||
}
|
||||
|
||||
/// Switch to a provider filter. Always fetches fresh data so the user never sees stale numbers.
|
||||
func switchTo(provider: ProviderFilter) async {
|
||||
selectedProvider = provider
|
||||
await refresh(includeOptimize: true)
|
||||
await refresh(includeOptimize: true, force: true)
|
||||
}
|
||||
|
||||
private var inFlightKeys: Set<PayloadCacheKey> = []
|
||||
|
|
@ -79,11 +79,15 @@ final class AppStore {
|
|||
/// Refresh the currently selected (period, provider) combination. Guards against concurrent
|
||||
/// fetches for the same key so a slow initial request can't overwrite a newer one that
|
||||
/// finished first (which would show stale numbers the user has already moved past).
|
||||
func refresh(includeOptimize: Bool) async {
|
||||
/// When `force` is false (background timer), skips the CLI call if the cache is still fresh.
|
||||
func refresh(includeOptimize: Bool, force: Bool = false) async {
|
||||
let key = currentKey
|
||||
if !force, cache[key]?.isFresh == true { return }
|
||||
guard !inFlightKeys.contains(key) else { return }
|
||||
inFlightKeys.insert(key)
|
||||
isLoading = true
|
||||
if cache[key] == nil {
|
||||
isLoading = true
|
||||
}
|
||||
defer {
|
||||
inFlightKeys.remove(key)
|
||||
isLoading = false
|
||||
|
|
@ -228,15 +232,21 @@ enum ProviderFilter: String, CaseIterable, Identifiable {
|
|||
case copilot = "Copilot"
|
||||
case gemini = "Gemini"
|
||||
case kiro = "Kiro"
|
||||
case kiloCode = "KiloCode"
|
||||
case openclaw = "OpenClaw"
|
||||
case opencode = "OpenCode"
|
||||
case pi = "Pi"
|
||||
case omp = "OMP"
|
||||
case rooCode = "Roo Code"
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var providerKeys: [String] {
|
||||
switch self {
|
||||
case .cursor: ["cursor", "cursor agent"]
|
||||
case .rooCode: ["roo-code"]
|
||||
case .kiloCode: ["kilo-code"]
|
||||
case .openclaw: ["openclaw"]
|
||||
default: [rawValue.lowercased()]
|
||||
}
|
||||
}
|
||||
|
|
@ -249,10 +259,13 @@ enum ProviderFilter: String, CaseIterable, Identifiable {
|
|||
case .cursor: "cursor"
|
||||
case .copilot: "copilot"
|
||||
case .gemini: "gemini"
|
||||
case .kiloCode: "kilo-code"
|
||||
case .kiro: "kiro"
|
||||
case .openclaw: "openclaw"
|
||||
case .opencode: "opencode"
|
||||
case .pi: "pi"
|
||||
case .omp: "omp"
|
||||
case .rooCode: "roo-code"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,10 +93,13 @@ extension ProviderFilter {
|
|||
case .cursor: return Theme.categoricalCursor
|
||||
case .copilot: return Color(red: 0x6D/255.0, green: 0x8F/255.0, blue: 0xA6/255.0)
|
||||
case .gemini: return Color(red: 0x44/255.0, green: 0x85/255.0, blue: 0xF4/255.0)
|
||||
case .kiloCode: return Color(red: 0x00/255.0, green: 0x96/255.0, blue: 0x88/255.0)
|
||||
case .kiro: return Color(red: 0x4A/255.0, green: 0x9E/255.0, blue: 0xC4/255.0)
|
||||
case .openclaw: return Color(red: 0xDA/255.0, green: 0x70/255.0, blue: 0x56/255.0)
|
||||
case .opencode: return Color(red: 0x5B/255.0, green: 0x83/255.0, blue: 0x5B/255.0)
|
||||
case .pi: return Color(red: 0xB2/255.0, green: 0x6B/255.0, blue: 0x3D/255.0)
|
||||
case .omp: return Color(red: 0x8B/255.0, green: 0x5C/255.0, blue: 0xB0/255.0)
|
||||
case .rooCode: return Color(red: 0x4C/255.0, green: 0xAF/255.0, blue: 0x50/255.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ struct FooterBar: View {
|
|||
.fixedSize()
|
||||
|
||||
Button {
|
||||
Task { await store.refresh(includeOptimize: true) }
|
||||
Task { await store.refresh(includeOptimize: true, force: true) }
|
||||
} label: {
|
||||
Image(systemName: store.isLoading ? "arrow.triangle.2.circlepath" : "arrow.clockwise")
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue