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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ More release automation notes live in [docs/release/ci-cd.md](docs/release/ci-cd
| WSL setup and auth tips | [docs/WSL.md](docs/WSL.md) |
| Browser cookie details | [docs/COOKIES.md](docs/COOKIES.md) |

## Local integrations

- [AI Usage Limits](https://github.com/lenadweb/stream-deck-ai-limits) — Elgato Stream Deck integration that can consume the local `codexbar serve` dashboard/API to show provider, account, quota, or payload metrics.

## Credits

- Original macOS app: [steipete/CodexBar](https://github.com/steipete/CodexBar) by Peter Steinberger
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"identifier": "usage-spend-save-dialog",
"description": "Allow the Settings window to choose a destination for Usage & Spend JSON exports.",
"windows": ["settings"],
"permissions": ["dialog:allow-save"]
}
19 changes: 19 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ impl RateWindowSnapshot {
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CostDailyPointBridge {
pub day: String,
pub amount: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CostSnapshotBridge {
Expand All @@ -93,6 +100,8 @@ pub struct CostSnapshotBridge {
pub balance: Option<f64>,
#[serde(default)]
pub formatted_balance: Option<String>,
#[serde(default)]
pub daily: Vec<CostDailyPointBridge>,
}

fn default_currency() -> String {
Expand Down Expand Up @@ -357,6 +366,14 @@ impl ProviderUsageSnapshot {
formatted_limit: c.format_limit(),
balance: c.balance,
formatted_balance: c.format_balance(),
daily: c
.daily
.iter()
.map(|point| CostDailyPointBridge {
day: point.day.clone(),
amount: point.amount,
})
.collect(),
}),
plan_name: usage.login_method.clone(),
account_email: usage.account_email.clone(),
Expand Down Expand Up @@ -654,6 +671,7 @@ pub struct SettingsSnapshot {
provider_usage_thresholds:
std::collections::HashMap<String, codexbar::settings::UsageThresholdOverride>,
predictive_pace_warning_enabled: bool,
show_pace: bool,
tray_icon_mode: &'static str,
switcher_shows_icons: bool,
menu_bar_shows_highest_usage: bool,
Expand Down Expand Up @@ -766,6 +784,7 @@ impl From<Settings> for SettingsSnapshot {
critical_usage_threshold: settings.critical_usage_threshold,
provider_usage_thresholds: settings.provider_usage_thresholds,
predictive_pace_warning_enabled: settings.predictive_pace_warning_enabled,
show_pace: settings.show_pace,
tray_icon_mode: tray_icon_mode_label(settings.tray_icon_mode),
switcher_shows_icons: settings.switcher_shows_icons,
menu_bar_shows_highest_usage: settings.menu_bar_shows_highest_usage,
Expand Down
28 changes: 28 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/provider_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,34 @@ pub fn set_provider_usage_source(provider_id: String, source: String) -> Result<
settings.save().map_err(|e| e.to_string())
}

// ── OpenRouter Management API key ────────────────────────────────────

#[tauri::command]
pub fn has_openrouter_management_api_key() -> bool {
Settings::load()
.management_api_token(ProviderId::OpenRouter)
.is_some()
}

#[tauri::command]
pub fn set_openrouter_management_api_key(api_key: String) -> Result<(), String> {
let trimmed = api_key.trim();
if trimmed.is_empty() {
return Err("Management API key must not be empty".to_string());
}
validate_single_line_secret(trimmed, "Management API key", MAX_API_KEY_LEN)?;
let mut settings = Settings::load();
settings.set_management_api_token(ProviderId::OpenRouter, Some(trimmed.to_string()));
settings.save().map_err(|error| error.to_string())
}

#[tauri::command]
pub fn remove_openrouter_management_api_key() -> Result<(), String> {
let mut settings = Settings::load();
settings.set_management_api_token(ProviderId::OpenRouter, None);
settings.save().map_err(|error| error.to_string())
}

// ── Per-provider cookie source + region ───────────────────────────────

/// Map a CLI-name string to a `ProviderId` whose cookie source is exposed in
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub(crate) fn build_fetch_context(
let api_key = active_token_api_key.or(stored_api_key);
let has_kimi_code_api_key =
id == ProviderId::Kimi && api_key.as_deref().is_some_and(|key| !key.trim().is_empty());
let has_opencodego_api_key = id == ProviderId::OpenCodeGo
&& api_key.as_deref().is_some_and(|key| !key.trim().is_empty());

let (mut source_mode, mut cookie_header) = if id.cookie_domain().is_none() {
let source_mode = if active_token_env.is_some() {
Expand All @@ -52,6 +54,9 @@ pub(crate) fn build_fetch_context(
"off" if has_kimi_code_api_key && usage_source == SourceMode::Auto => {
(SourceMode::Auto, None)
}
"off" if has_opencodego_api_key && usage_source == SourceMode::Auto => {
(SourceMode::Auto, None)
}
// Droid/Factory: cookie-off must never scrape browser cookies. Map to
// Cli (API-only in the provider) so Auto does not fall through to web.
"off" if id == ProviderId::Factory => (SourceMode::Cli, None),
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct SettingsUpdate {
pub provider_usage_thresholds:
Option<std::collections::HashMap<String, codexbar::settings::UsageThresholdOverride>>,
pub predictive_pace_warning_enabled: Option<bool>,
pub show_pace: Option<bool>,
pub tray_icon_mode: Option<String>,
pub switcher_shows_icons: Option<bool>,
pub menu_bar_shows_highest_usage: Option<bool>,
Expand Down Expand Up @@ -272,6 +273,9 @@ impl SettingsUpdate {
if let Some(v) = self.predictive_pace_warning_enabled {
settings.predictive_pace_warning_enabled = v;
}
if let Some(v) = self.show_pace {
settings.show_pace = v;
}
Ok(self)
}

Expand Down
21 changes: 21 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,27 @@ fn fetch_context_kimi_api_key_preserves_auto_for_web_fallback() {
assert_eq!(ctx.api_key.as_deref(), Some("sk-kimi-test"));
}

#[test]
fn fetch_context_opencodego_api_key_preserves_auto_for_api_overlay() {
let settings = Settings::default();
let cookies = ManualCookies::default();
let mut api_keys = ApiKeys::default();
api_keys.set("opencodego", "go-test", None);
let token_accounts = HashMap::new();

let ctx = super::build_fetch_context(
ProviderId::OpenCodeGo,
&settings,
&cookies,
&api_keys,
&token_accounts,
);

assert_eq!(ctx.source_mode, SourceMode::Auto);
assert!(ctx.manual_cookie_header.is_none());
assert_eq!(ctx.api_key.as_deref(), Some("go-test"));
}

#[test]
fn fetch_context_includes_minimax_region() {
let mut settings = Settings::default();
Expand Down
Loading
Loading