Skip to content
Open
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
24 changes: 24 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ pub struct SubscriptionMetadataSnapshot {
pub renews_at: Option<String>,
}

/// Display-only provider inventory. Redemption identifiers never cross the
/// bridge.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderInventoryItemSnapshot {
pub id: String,
pub title: String,
pub available_count: u32,
pub next_expires_at: Option<String>,
}

/// A frontend-friendly snapshot of one provider's usage data.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand All @@ -200,6 +211,8 @@ pub struct ProviderUsageSnapshot {
pub tertiary: Option<RateWindowSnapshot>,
#[serde(default)]
pub extra_rate_windows: Vec<NamedRateWindowSnapshot>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub inventory: Vec<ProviderInventoryItemSnapshot>,
#[serde(default)]
pub cost: Option<CostSnapshotBridge>,
#[serde(default)]
Expand Down Expand Up @@ -384,6 +397,16 @@ impl ProviderUsageSnapshot {
window: RateWindowSnapshot::from_rate_window(&extra.window),
})
.collect(),
inventory: result
.inventory
.iter()
.map(|item| ProviderInventoryItemSnapshot {
id: item.id.clone(),
title: item.title.clone(),
available_count: item.available_count,
next_expires_at: item.next_expires_at.map(|date| date.to_rfc3339()),
})
.collect(),
cost: result.cost.as_ref().map(|c| CostSnapshotBridge {
used: c.used,
limit: c.limit,
Expand Down Expand Up @@ -461,6 +484,7 @@ impl ProviderUsageSnapshot {
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
plan_name: None,
account_email: None,
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct ProviderDetail {
pub model_specific: Option<RateWindowSnapshot>,
pub tertiary: Option<RateWindowSnapshot>,
pub extra_rate_windows: Vec<NamedRateWindowSnapshot>,
pub inventory: Vec<ProviderInventoryItemSnapshot>,

// Cost / pace.
pub cost: Option<CostSnapshotBridge>,
Expand Down Expand Up @@ -91,6 +92,7 @@ pub(crate) fn build_provider_detail(provider_id: &str) -> Result<ProviderDetail,
model_specific: None,
tertiary: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
pace: None,
last_error: None,
Expand Down Expand Up @@ -154,6 +156,7 @@ pub fn get_provider_detail(
detail.model_specific = snapshot.model_specific.clone();
detail.tertiary = snapshot.tertiary.clone();
detail.extra_rate_windows = snapshot.extra_rate_windows.clone();
detail.inventory = snapshot.inventory.clone();
detail.cost = snapshot.cost.clone();
detail.pace = snapshot.pace.clone();
}
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,7 @@ mod reset_backfill_tests {
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
plan_name: None,
account_email: None,
Expand Down
44 changes: 42 additions & 2 deletions apps/desktop-tauri/src-tauri/src/commands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::state::AppState;
use crate::surface::SurfaceMode;
use crate::surface_target::SurfaceTarget;
use codexbar::core::{
FetchContext, ProviderAccountData, ProviderError, ProviderFetchResult, ProviderId, SourceMode,
TokenAccount, instantiate_provider,
FetchContext, ProviderAccountData, ProviderError, ProviderFetchResult, ProviderId,
ProviderInventoryItem, SourceMode, TokenAccount, instantiate_provider,
};
use codexbar::host::session::launch_block_reason;
use codexbar::settings::{ApiKeys, Language, ManualCookies, Settings};
Expand Down Expand Up @@ -981,6 +981,34 @@ fn local_opencodego_estimates_keep_quota_windows_but_drop_derived_pace() {
);
}

#[test]
fn provider_inventory_maps_to_the_bridge_without_token_ids() {
let expiry = chrono::DateTime::<chrono::Utc>::from_timestamp(1_900_000_000, 0).unwrap();
let result = ProviderFetchResult::new(
codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(12.0)),
"web",
)
.with_inventory_item(ProviderInventoryItem {
id: "reset-credits".to_string(),
title: "Limit Reset Credits".to_string(),
available_count: 2,
next_expires_at: Some(expiry),
});
let metadata = instantiate_provider(ProviderId::Grok).metadata().clone();
let snapshot =
ProviderUsageSnapshot::from_fetch_result(ProviderId::Grok, &metadata, &result, None);

assert_eq!(snapshot.inventory.len(), 1);
assert_eq!(snapshot.inventory[0].available_count, 2);
assert_eq!(
snapshot.inventory[0].next_expires_at.as_deref(),
Some("2030-03-17T17:46:40+00:00")
);
let serialized = serde_json::to_string(&snapshot).unwrap();
assert!(serialized.contains("reset-credits"));
assert!(!serialized.contains("coupon-token-secret"));
}

#[test]
fn provider_cache_is_fresh_inside_stale_window() {
assert!(super::is_provider_cache_fresh(
Expand Down Expand Up @@ -1057,6 +1085,7 @@ fn provider_cache_upsert_replaces_existing_provider() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(10.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "CLI".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand All @@ -1083,6 +1112,7 @@ fn provider_cache_prunes_disabled_providers() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(10.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "CLI".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1116,6 +1146,7 @@ fn hiding_codex_spark_rows_preserves_other_extra_usage() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(10.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "CLI".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1149,6 +1180,7 @@ fn claude_transient_auth_failure_preserves_first_last_good_snapshot() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(42.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1184,6 +1216,7 @@ fn codex_transient_transport_failure_helper_uses_typed_policy() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(42.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1218,6 +1251,7 @@ fn claude_repeated_auth_failure_surfaces_error() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(42.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1259,6 +1293,7 @@ fn claude_cloudflare_challenge_retains_prior_usage_while_surfaceing_guidance() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(42.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1311,6 +1346,7 @@ fn claude_cloudflare_challenge_keeps_prior_usage_when_guidance_surfaces() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(42.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "Web".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1360,6 +1396,7 @@ fn claude_cli_parse_failure_keeps_last_good_every_time() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(17.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "CLI".to_string(),
has_successful_claude_cli_quota: true,
pace_authoritative: true,
Expand Down Expand Up @@ -1405,6 +1442,7 @@ fn claude_hard_credentials_missing_does_not_preserve_stale() {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(17.0)),
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1575,6 +1613,7 @@ fn japanese_provider_snapshot_localizes_weekly_label() {
usage,
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
Expand Down Expand Up @@ -1607,6 +1646,7 @@ fn japanese_provider_snapshot_localizes_pace_reserve_description() {
usage,
cost: None,
wayfinder_usage: None,
inventory: Vec::new(),
source_label: "OAuth".to_string(),
has_successful_claude_cli_quota: false,
pace_authoritative: true,
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 @@ -195,6 +195,7 @@ mod tests {
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
plan_name: Some("Team".to_string()),
account_email: Some("dev@example.com".to_string()),
Expand Down Expand Up @@ -243,6 +244,7 @@ mod tests {
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
plan_name: None,
account_email: None,
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/tray_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ mod tests {
}),
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: cost.map(|(used, limit)| crate::commands::CostSnapshotBridge {
used,
limit: Some(limit),
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/usage_metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ mod tests {
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
cost: None,
plan_name: None,
account_email: None,
Expand Down
51 changes: 50 additions & 1 deletion apps/desktop-tauri/src/components/MenuCardDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
CostSummaryDisplayStyle,
DailyCostPoint,
PaceSnapshot,
ProviderInventoryItem,
ProviderChartData,
ProviderLocalUsageSummary,
ProviderUsageSnapshot,
Expand Down Expand Up @@ -413,6 +414,7 @@ function MetricRow({

export interface MenuCardPresence {
hasMetrics: boolean;
hasInventory: boolean;
hasCost: boolean;
hasPace: boolean;
hasCharts: boolean;
Expand Down Expand Up @@ -456,6 +458,7 @@ export function describeCard(
const localUsage = provider.error ? null : chartData?.localUsage ?? null;
const wayfinderUsage = isWayfinder ? provider.wayfinderUsage : null;
const hasMetrics = visibleMetrics.length > 0;
const hasInventory = !provider.error && (provider.inventory?.length ?? 0) > 0;
const hasCost =
!!provider.cost &&
(costSummaryDisplayStyle !== "hidden" || provider.cost.alwaysVisible === true);
Expand All @@ -465,9 +468,16 @@ export function describeCard(
!!provider.pace;
const hasDetails =
!provider.error &&
(hasMetrics || hasCost || hasPace || hasCharts || !!localUsage || !!wayfinderUsage);
(hasMetrics ||
hasInventory ||
hasCost ||
hasPace ||
hasCharts ||
!!localUsage ||
!!wayfinderUsage);
return {
hasMetrics,
hasInventory,
hasCost,
hasPace,
hasCharts,
Expand Down Expand Up @@ -505,6 +515,7 @@ export default function MenuCardDetails({

const {
hasMetrics,
hasInventory,
hasCost,
hasPace,
hasCharts,
Expand Down Expand Up @@ -540,6 +551,18 @@ export default function MenuCardDetails({
</section>
)}

{!provider.error && hasInventory && (
<section className="menu-card__group menu-card__inventory">
{provider.inventory?.map((item) => (
<InventoryItemRow
key={item.id}
item={item}
resetTimeRelative={display.resetTimeRelative}
/>
))}
</section>
)}

{wayfinderUsage && <WayfinderUsageBlock usage={wayfinderUsage} />}

{hasMetrics && hasCost && <div className="menu-card__divider" />}
Expand Down Expand Up @@ -712,3 +735,29 @@ export default function MenuCardDetails({
</div>
);
}

function InventoryItemRow({
item,
resetTimeRelative,
}: {
item: ProviderInventoryItem;
resetTimeRelative: boolean;
}) {
const formattedExpiry = useFormattedResetTime(
item.nextExpiresAt,
null,
resetTimeRelative,
"expires",
);

return (
<div className="menu-card__cost-line">
<span>{item.title}: {item.availableCount} available</span>
{formattedExpiry && (
<span className="menu-card__cost-line--muted">
{formattedExpiry}
</span>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,27 @@ describe("UsageSection", () => {
expect(label.parentElement).toHaveTextContent("No active 5h session");
expect(label.parentElement?.querySelector(".provider-usage-bar__track")).toBeNull();
});

it("renders discrete inventory without turning it into a quota bar", async () => {
const detail = provider();
detail.session = null;
detail.extraRateWindows = [];
detail.inventory = [
{
id: "reset-credits",
title: "Limit Reset Credits",
availableCount: 2,
nextExpiresAt: "2099-01-01T00:00:00Z",
},
];

const { container } = render(
<LocaleProvider>
<UsageSection provider={detail} resetTimeRelative={true} t={(key) => key} />
</LocaleProvider>,
);

expect(await screen.findByText(/Limit Reset Credits: 2 available/)).toBeInTheDocument();
expect(container.querySelector(".provider-usage-bar__track")).toBeNull();
});
});
Loading