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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# Changelog

## [Windows] 0.46.0 - 2026-07-30

Windows port of upstream CodexBar **0.45.2 → 0.46.0**. macOS-only shell items (WidgetKit, Sparkle, AppKit menu layout, Homebrew, Safari cookie APIs) remain deferred.

### Added
- Providers: Qwen Cloud (Individual Token Plans with 5-hour and weekly rolling windows) and ZoomMate (credit status with host failover, cURL/cookie auth, bearer mint).
- Alibaba Token Plan: Personal/Solo variants for mainland (Bailian) and international (Model Studio) accounts via `alibaba_token_plan_region` setting.
- Claude: prepaid credit balance in cost surfaces from cached/manual web sessions.
- Claude: setting to hide the Daily Routines row (`claude_daily_routines_usage_visible`, default on).
- Menu: fractional session-quota estimates on the weekly row ("Estimated: N session quotas left") when session+weekly history qualifies; optional `weekly_progress_work_days`.
- Codex: local Workspaces indexing foundation — per-project/session/model usage attribution with a local sidecar index; new `codexbar workspaces` CLI and desktop snapshot bridge.
- CLI: `config dump` redacts stored credentials by default; `--show-secrets` restores raw output.

### Changed
- Codex local cost scans use the disk cache: unchanged files skipped by mtime/size, grown logs resumed mid-file, 256 KiB line bound — repeat scans are incremental.
- Automatic tray metric surfaces the highest-used (exhausted) window across providers, preserving per-provider overrides.
- CLI alias `qwen` now resolves to Qwen Cloud (use `alibaba` for the Coding Plan).

### Fixed
- Claude: model-scoped weekly rows above Daily Routines; automatic metric prefers account Weekly over exhausted model carve-outs; learned full-session estimate stays visible while the session window is idle.
- Amp: subscription plans (e.g. Megawatt) parse into Other/Orb percentage windows instead of a misleading cookie error.
- Grok: explicit cookie refresh with validated session caching for background reuse.
- Chutes: quota counts render as detail text instead of being misread as reset schedules.
- LLMProxy: skip already-elapsed reset times when picking the next reset.
- Ollama: reuse validated browser sessions across refreshes.

---

## [Windows] 0.45.3 - 2026-07-29

### Added
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/desktop-tauri/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "desktop-tauri",
"private": true,
"version": "0.45.3",
"version": "0.46.0",
"packageManager": "pnpm@10.18.1",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop-tauri/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "codexbar-desktop-tauri"
version = "0.45.3"
version = "0.46.0"
edition = "2024"
publish = false

Expand Down
54 changes: 54 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ pub struct CostSnapshotBridge {
pub resets_at: Option<String>,
pub formatted_used: String,
pub formatted_limit: Option<String>,
pub balance: Option<f64>,
pub formatted_balance: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
Expand All @@ -83,6 +85,18 @@ pub struct PaceSnapshot {
pub actual_used_percent: f64,
}

/// Session-equivalent weekly forecast for Claude/Codex menu secondary line.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionEquivalentForecastSnapshot {
pub estimated_windows_to_exhaust_weekly: f64,
pub windows_until_reset: i64,
pub available_windows_until_reset: f64,
pub sample_count: usize,
pub weekly_resets_at: String,
pub weekly_used_percent: f64,
}

/// A frontend-friendly snapshot of one provider's usage data.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
Expand All @@ -107,6 +121,8 @@ pub struct ProviderUsageSnapshot {
pub tray_status_label: Option<String>,
pub fetch_duration_ms: Option<u128>,
pub wayfinder_usage: Option<codexbar::core::WayfinderUsageSnapshot>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_equivalent_forecast: Option<SessionEquivalentForecastSnapshot>,
}

pub(crate) fn filter_hidden_codex_spark_rows(
Expand Down Expand Up @@ -168,6 +184,9 @@ impl ProviderUsageSnapshot {
s
});

let session_equivalent_forecast =
session_equivalent_forecast_for(id, &usage.primary, usage.secondary.as_ref());

Self {
provider_id: id.cli_name().to_string(),
display_name: id.display_name().to_string(),
Expand Down Expand Up @@ -204,6 +223,8 @@ impl ProviderUsageSnapshot {
resets_at: c.resets_at.map(|dt| dt.to_rfc3339()),
formatted_used: c.format_used(),
formatted_limit: c.format_limit(),
balance: c.balance,
formatted_balance: c.format_balance(),
}),
plan_name: usage.login_method.clone(),
account_email: usage.account_email.clone(),
Expand All @@ -215,6 +236,7 @@ impl ProviderUsageSnapshot {
tray_status_label: None,
fetch_duration_ms: None,
wayfinder_usage: result.wayfinder_usage.clone(),
session_equivalent_forecast,
}
}

Expand Down Expand Up @@ -253,10 +275,36 @@ impl ProviderUsageSnapshot {
tray_status_label: None,
fetch_duration_ms: None,
wayfinder_usage: None,
session_equivalent_forecast: None,
}
}
}

fn session_equivalent_forecast_for(
id: ProviderId,
session: &RateWindow,
weekly: Option<&RateWindow>,
) -> Option<SessionEquivalentForecastSnapshot> {
if !matches!(id, ProviderId::Claude | ProviderId::Codex) {
return None;
}
let weekly = weekly?;
let now = chrono::Utc::now();
let provider_id = id.cli_name();
codexbar::core::record_provider_windows(provider_id, session, Some(weekly), now);
let work_days = Settings::load().weekly_progress_work_days;
let forecast =
codexbar::core::forecast_for_provider(provider_id, session, weekly, now, work_days)?;
Some(SessionEquivalentForecastSnapshot {
estimated_windows_to_exhaust_weekly: forecast.estimated_windows_to_exhaust_weekly,
windows_until_reset: forecast.windows_until_reset,
available_windows_until_reset: forecast.available_windows_until_reset,
sample_count: forecast.sample_count,
weekly_resets_at: forecast.weekly_resets_at.to_rfc3339(),
weekly_used_percent: forecast.weekly_used_percent,
})
}

/// Build a compact tray status label from a raw snapshot using the current language.
/// Localization is done at render time so cached snapshots stay language-neutral.
pub(crate) fn compact_tray_status_label(
Expand Down Expand Up @@ -464,6 +512,9 @@ pub struct SettingsSnapshot {
float_bar_show_reset_inline: bool,
float_bar_show_cost: bool,
promote_tray_icon: bool,
claude_daily_routines_usage_visible: bool,
alibaba_token_plan_region: String,
weekly_progress_work_days: Option<u8>,
}

#[tauri::command]
Expand Down Expand Up @@ -564,6 +615,9 @@ impl From<Settings> for SettingsSnapshot {
float_bar_show_reset_inline: settings.float_bar_show_reset_inline,
float_bar_show_cost: settings.float_bar_show_cost,
promote_tray_icon: settings.promote_tray_icon,
claude_daily_routines_usage_visible: settings.claude_daily_routines_usage_visible,
alibaba_token_plan_region: settings.alibaba_token_plan_region,
weekly_progress_work_days: settings.weekly_progress_work_days,
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/codex_workspaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Codex local Workspaces snapshot bridge.

use codexbar::codex_workspaces::{CodexLocalProjectUsageSnapshot, CodexWorkspacesIndex};
use codexbar::settings::Settings;
use tauri::State;

use crate::state::AppState;
use std::sync::Mutex;

/// Return the local Codex workspaces usage snapshot.
///
/// Privacy-redacts paths/titles when `settings.hide_personal_info` is set.
#[tauri::command]
pub async fn get_codex_workspaces_snapshot(
_state: State<'_, Mutex<AppState>>,
force_refresh: Option<bool>,
history_days: Option<u32>,
) -> Result<CodexLocalProjectUsageSnapshot, String> {
let force = force_refresh.unwrap_or(false);
let days = history_days.unwrap_or(30);
let hide = Settings::load().hide_personal_info;

tauri::async_runtime::spawn_blocking(move || {
let index = CodexWorkspacesIndex::new(days);
let mut snapshot = index
.load_snapshot(force, |_| {})
.map_err(|e| e.to_string())?;
if hide {
snapshot.redact_for_privacy();
}
Ok(snapshot)
})
.await
.map_err(|e| format!("workspaces worker failed: {e}"))?
}
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod usage_spend;
mod agent_sessions;
mod bridge;
mod browser_import;
mod codex_workspaces;
mod credential_detection;
mod credentials;
mod locale_cmd;
Expand All @@ -44,6 +45,7 @@ mod system;
pub use agent_sessions::*;
pub(crate) use bridge::*;
pub use browser_import::*;
pub use codex_workspaces::*;
pub use credential_detection::*;
pub use credentials::*;
pub use locale_cmd::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn region_provider(provider_id: &str) -> Option<codexbar::core::ProviderId> {
use codexbar::core::ProviderId;
Some(match provider_id {
"alibaba" => ProviderId::Alibaba,
"alibabatokenplan" => ProviderId::AlibabaTokenPlan,
"zai" => ProviderId::Zai,
"minimax" => ProviderId::MiniMax,
_ => return None,
Expand Down Expand Up @@ -586,6 +587,14 @@ pub fn region_options_for(provider_id: &str) -> Vec<RegionOption> {
.to_string(),
},
],
"alibabatokenplan" => codexbar::providers::AlibabaTokenPlanRegion::ALL
.iter()
.copied()
.map(|region| RegionOption {
value: region.as_str().to_string(),
label: region.display_name().to_string(),
})
.collect(),
_ => Vec::new(),
}
}
Expand Down
19 changes: 19 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,17 @@ pub struct SettingsUpdate {
pub float_bar_show_reset_inline: Option<bool>,
pub float_bar_show_cost: Option<bool>,
pub promote_tray_icon: Option<bool>,
pub claude_daily_routines_usage_visible: Option<bool>,
pub alibaba_token_plan_region: Option<String>,
pub weekly_progress_work_days: Option<u8>,
}

impl SettingsUpdate {
fn refreshes_provider_data(&self) -> bool {
self.enabled_providers.is_some()
|| self.claude_daily_routines_usage_visible.is_some()
|| self.alibaba_token_plan_region.is_some()
|| self.weekly_progress_work_days.is_some()
}

fn notifies_float_bar(&self) -> bool {
Expand Down Expand Up @@ -292,6 +298,19 @@ impl SettingsUpdate {
settings.set_claude_avoid_keychain_prompts(true);
}
}
if let Some(v) = self.claude_daily_routines_usage_visible {
settings.claude_daily_routines_usage_visible = v;
}
if let Some(v) = self.alibaba_token_plan_region.as_deref() {
let region = codexbar::providers::AlibabaTokenPlanRegion::from_settings_value(Some(v));
settings.set_api_region(
codexbar::core::ProviderId::AlibabaTokenPlan,
region.as_str(),
);
}
if let Some(v) = self.weekly_progress_work_days {
settings.weekly_progress_work_days = if (2..=6).contains(&v) { Some(v) } else { None };
}
self
}

Expand Down
17 changes: 17 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,23 @@ fn region_options_for_regional_provider() {
assert_eq!(values, vec!["singapore", "us", "germany", "hongkong", "cn"]);
}

#[test]
fn alibaba_token_plan_region_options() {
let opts = super::region_options_for("alibabatokenplan");
let values: Vec<_> = opts.iter().map(|o| o.value.as_str()).collect();
let labels: Vec<_> = opts.iter().map(|o| o.label.as_str()).collect();
assert_eq!(values, vec!["cn", "intl", "cn-personal", "intl-personal"]);
assert_eq!(
labels,
vec![
"China Team",
"International Team",
"China Personal/Solo",
"International Personal/Solo"
]
);
}

#[test]
fn minimax_region_options_match_upstream_hosts() {
let opts = super::region_options_for("minimax");
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ fn main() {
commands::get_provider_chart_data,
commands::get_provider_local_usage_summary,
commands::get_usage_spend_summary,
commands::get_codex_workspaces_snapshot,
commands::reorder_providers,
commands::set_provider_cookie_source,
commands::get_provider_cookie_source_options,
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/powertoys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ mod tests {
tray_status_label: None,
fetch_duration_ms: None,
wayfinder_usage: None,
session_equivalent_forecast: None,
});
let value = serde_json::to_value(snapshot).unwrap();

Expand Down Expand Up @@ -248,6 +249,7 @@ mod tests {
tray_status_label: None,
fetch_duration_ms: None,
wayfinder_usage: None,
session_equivalent_forecast: None,
});
let value = serde_json::to_value(snapshot).unwrap();

Expand Down
Loading
Loading