usage-cli 0.34.0__tar.gz → 0.34.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usage_cli-0.34.0/usage_cli.egg-info → usage_cli-0.34.2}/PKG-INFO +2 -2
- {usage_cli-0.34.0 → usage_cli-0.34.2}/i18n.json +25 -10
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/muse_quota_probe.py +2 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/app.py +1 -9
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/menu.py +11 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/refresh.py +8 -12
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/state.py +28 -43
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/payload.py +7 -4
- {usage_cli-0.34.0 → usage_cli-0.34.2}/pyproject.toml +2 -2
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_analyzer_pipeline.py +1 -1
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_compact_cards.py +3 -1
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_desktop_quota_display.py +2 -3
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar.py +29 -31
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_grok.py +2 -3
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_notify.py +3 -4
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_muse_quota_probe.py +11 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_packaged_resources.py +2 -1
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_panels.py +3 -3
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_web_panel_payload.py +1 -1
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_wintray.py +6 -5
- {usage_cli-0.34.0 → usage_cli-0.34.2/usage_cli.egg-info}/PKG-INFO +2 -2
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_cli.egg-info/requires.txt +1 -1
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/app.py +20 -31
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/menu.py +2 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/LICENSE +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/README.md +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/THIRD_PARTY_NOTICES.md +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/agy.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/claude.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/codex.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/grok.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/muse.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/rate_limits.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/registry.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/adapters/types.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/aggregator.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/blocks.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/diagnoser.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/diagnosis_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/dispatch_ledger.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/history_persist.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/insights.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/ledger_backfill.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/persona_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/reporter.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/subscription.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/analyzer/usage_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/claude_pane.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/codex_hook_trust.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/login_item.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/session_hooks.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/setup_hook.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/installer/statusline_settings.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/_shared_cache_file.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/agy_disk_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/agy_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/agy_quota_probe.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/cache_quarantine.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/chromium_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/claude_desktop.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/claude_paths.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/claude_usage.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/codex_disk_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/codex_events.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/codex_fork_replay.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/codex_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/codex_paths.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/disk_cache_common.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/disk_cache_lifecycle.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/grok_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/grok_quota_probe.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/history_disk_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/history_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/jsonl_limits.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/jsonl_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/loaders/muse_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/main.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/actions.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/agy.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/chrome.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/fsevents_watch.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/grok.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/manual_refresh.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/muse.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/new_badge.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/notify.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/popover.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/prefs.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/title.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/update.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/menubar/whats_new.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/base.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/catalog.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/dynamic_height.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/panel_scale.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/panel_window.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/panel_window_state.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/web_panel.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/panels/window_drag.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/agy_window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/burn_rate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/codex_window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/keeper_outcome.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/notifications.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/quota_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/status_payload.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/usage_rate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/quota/window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/setup.cfg +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/setup_app.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_adapters_claude.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_adapters_codex.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_adapters_rate_limits.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_aggregator.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_agy_adapter.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_agy_disk_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_agy_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_agy_quota_probe.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_agy_window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_analyzer_blocks.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_app_version.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_burn_rate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_cache_quarantine.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_check_upstream.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_chromium_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_claude_desktop.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_claude_pane.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_claude_paths.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_codex_consistency.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_codex_hook_trust.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_codex_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_codex_window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_desktop_fresh_cache.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_desktop_quota_review.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_diagnoser.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_disk_cache_shards.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_dispatch_ledger.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_doctor.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_dynamic_height.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_fsevents_watch.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_grok_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_grok_quota_probe.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_history_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_history_persist.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_hook_scripts_are_standalone.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_html_report_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_i18n_format_fallback.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_i18n_key_parity.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_i18n_packaged_path.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_insights.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_jsonl_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_keeper_menu.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_keeper_outcome.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_ledger_backfill.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_login_item.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_main.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_manual_refresh.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_agy.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_muse.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_prefs.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_state.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_menubar_update.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_muse_coverage.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_muse_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_muse_review_regressions.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_new_badge.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_packaging.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_panel_footer_layout.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_panel_scale.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_panel_window_state.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_panels_lazy_import.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_persona_loader.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_pricing.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_project_resolver.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_quota_aware.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_quota_label.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_report_cold_read.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_report_daily_chart.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_report_filter_js.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_report_share_mask.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_reporter_build_data.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_service_status.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_session_resume.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_setup_app.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_setup_hook.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_setup_hook_coexistence.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_setup_hook_resume.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_setup_hook_terse.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_subprocess_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_subscription.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_taskbar_menu.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_time_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_tui.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_tui_sprite.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_ui_tables.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_update_checker.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_update_gate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_update_release_notes.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_cli.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_client.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_dir_sweeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_lang.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_logging.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_notifications.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_rate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_snapshot.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_snapshot_replay.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_statusline.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_statusline_agy.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_statusline_forwarder.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_statusline_grok.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_terse_mode.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_usage_terse_reminder.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_whats_new.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_wheel_packaging.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_win_login_item.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_window_keeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tests/test_windows_manifest.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tui/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tui/app.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/tui/sprite.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/html_report.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_charts.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_daily_chart.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_filter.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_ledger.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_scripts.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/report_styles.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/ui/tables.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/updates/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/updates/checker.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/updates/gate.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/updates/release_notes.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/updates/whats_new.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_app/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_app/cli.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_app/doctor.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_cli.egg-info/SOURCES.txt +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_cli.egg-info/dependency_links.txt +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_cli.egg-info/entry_points.txt +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_cli.egg-info/top_level.txt +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/app_version.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/i18n.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/prefs.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/pricing.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/project_resolver.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/service_status.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/subprocess_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/time_utils.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/usage_dir_sweeper.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/usage_lang.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_common/usage_logging.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_quota_aware.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_session_resume.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_statusline.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_statusline_agy.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_statusline_forwarder.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_statusline_grok.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_terse_mode.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/usage_hooks/usage_terse_reminder.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/__init__.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/login_item.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/prefs.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/quota_label.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/taskbar_menu.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/taskbar_obstacles.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/taskbar_overlay.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/watch.py +0 -0
- {usage_cli-0.34.0 → usage_cli-0.34.2}/wintray/whats_new.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usage-cli
|
|
3
|
-
Version: 0.34.
|
|
3
|
+
Version: 0.34.2
|
|
4
4
|
Summary: usage — 在 macOS menu bar 與 Windows 系統匣顯示 Claude Code、Codex、Antigravity 用量的小工具(也提供終端機 TUI)
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
Requires-Python: >=3.13
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pyobjc-framework-WebKit>=11.0; sys_platform == "darwin"
|
|
|
15
15
|
Requires-Dist: rich<16.0.0,>=15.0.0
|
|
16
16
|
Provides-Extra: windows
|
|
17
17
|
Requires-Dist: pystray>=0.19.5; sys_platform == "win32" and extra == "windows"
|
|
18
|
-
Requires-Dist: pillow>=
|
|
18
|
+
Requires-Dist: pillow>=13.0.0; sys_platform == "win32" and extra == "windows"
|
|
19
19
|
Requires-Dist: pywebview>=5.4; sys_platform == "win32" and extra == "windows"
|
|
20
20
|
Requires-Dist: windows-toasts>=1.3.1; sys_platform == "win32" and extra == "windows"
|
|
21
21
|
Dynamic: license-file
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"panel_world_cup": "世界盃 2026",
|
|
20
20
|
"panel_ai_daily": "AI 更新日報",
|
|
21
21
|
"switch_panel": "更換面板",
|
|
22
|
+
"settings_button": "⚙ 設定",
|
|
23
|
+
"statusline_sync_menu": "狀態列同步額度",
|
|
24
|
+
"statusline_sync_tooltip": "從 Claude Code 的狀態列讀取額度。關掉後,Claude 的額度就不會再更新。",
|
|
22
25
|
"launch_at_login": "開機自動啟動",
|
|
23
26
|
"reset_panel_position": "重設面板位置",
|
|
24
27
|
"hide_sections_menu": "隱藏區塊",
|
|
@@ -74,8 +77,8 @@
|
|
|
74
77
|
"service_degraded": "效能降級",
|
|
75
78
|
"service_partial_outage": "部分中斷",
|
|
76
79
|
"service_major_outage": "大規模中斷",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
80
|
+
"today_label": "今日:",
|
|
81
|
+
"yesterday_label": "昨日:",
|
|
79
82
|
"percent_used": "{value}% 已用",
|
|
80
83
|
"reset_in": "重置 {time}",
|
|
81
84
|
"reset_imminent": "即將重置",
|
|
@@ -578,6 +581,9 @@
|
|
|
578
581
|
"panel_world_cup": "World Cup 2026",
|
|
579
582
|
"panel_ai_daily": "AI Update Daily",
|
|
580
583
|
"switch_panel": "Switch Panel",
|
|
584
|
+
"settings_button": "⚙ Settings",
|
|
585
|
+
"statusline_sync_menu": "Sync quota via status line",
|
|
586
|
+
"statusline_sync_tooltip": "Reads the Claude quota from Claude Code's status line. Turn it off and the Claude quota stops updating.",
|
|
581
587
|
"launch_at_login": "Launch at Login",
|
|
582
588
|
"reset_panel_position": "Reset panel position",
|
|
583
589
|
"hide_sections_menu": "Hide Sections",
|
|
@@ -633,8 +639,8 @@
|
|
|
633
639
|
"service_degraded": "Degraded performance",
|
|
634
640
|
"service_partial_outage": "Partial outage",
|
|
635
641
|
"service_major_outage": "Major outage",
|
|
636
|
-
"
|
|
637
|
-
"
|
|
642
|
+
"today_label": "Today:",
|
|
643
|
+
"yesterday_label": "Yesterday:",
|
|
638
644
|
"percent_used": "{value}% used",
|
|
639
645
|
"reset_in": "Resets in {time}",
|
|
640
646
|
"reset_imminent": "Reset imminent",
|
|
@@ -1137,6 +1143,9 @@
|
|
|
1137
1143
|
"panel_world_cup": "世界杯 2026",
|
|
1138
1144
|
"panel_ai_daily": "AI 更新日报",
|
|
1139
1145
|
"switch_panel": "切换面板",
|
|
1146
|
+
"settings_button": "⚙ 设置",
|
|
1147
|
+
"statusline_sync_menu": "状态栏同步额度",
|
|
1148
|
+
"statusline_sync_tooltip": "从 Claude Code 的状态栏读取额度。关闭后,Claude 的额度就不会再更新。",
|
|
1140
1149
|
"launch_at_login": "开机自动启动",
|
|
1141
1150
|
"reset_panel_position": "重设面板位置",
|
|
1142
1151
|
"hide_sections_menu": "隐藏区块",
|
|
@@ -1192,8 +1201,8 @@
|
|
|
1192
1201
|
"service_degraded": "性能降级",
|
|
1193
1202
|
"service_partial_outage": "部分中断",
|
|
1194
1203
|
"service_major_outage": "大规模中断",
|
|
1195
|
-
"
|
|
1196
|
-
"
|
|
1204
|
+
"today_label": "今日:",
|
|
1205
|
+
"yesterday_label": "昨天:",
|
|
1197
1206
|
"percent_used": "{value}% 已用",
|
|
1198
1207
|
"reset_in": "重置 {time}",
|
|
1199
1208
|
"reset_imminent": "即将重置",
|
|
@@ -1696,6 +1705,9 @@
|
|
|
1696
1705
|
"panel_world_cup": "ワールドカップ 2026",
|
|
1697
1706
|
"panel_ai_daily": "AI更新日報",
|
|
1698
1707
|
"switch_panel": "パネル切替",
|
|
1708
|
+
"settings_button": "⚙ 設定",
|
|
1709
|
+
"statusline_sync_menu": "ステータスラインで使用量を同期",
|
|
1710
|
+
"statusline_sync_tooltip": "Claude Code のステータスラインから使用量を読み取ります。オフにすると Claude の使用量は更新されなくなります。",
|
|
1699
1711
|
"launch_at_login": "ログイン時に起動",
|
|
1700
1712
|
"reset_panel_position": "パネルの位置をリセット",
|
|
1701
1713
|
"hide_sections_menu": "セクションを非表示",
|
|
@@ -1751,8 +1763,8 @@
|
|
|
1751
1763
|
"service_degraded": "パフォーマンス低下",
|
|
1752
1764
|
"service_partial_outage": "一部障害",
|
|
1753
1765
|
"service_major_outage": "大規模障害",
|
|
1754
|
-
"
|
|
1755
|
-
"
|
|
1766
|
+
"today_label": "今日:",
|
|
1767
|
+
"yesterday_label": "昨日:",
|
|
1756
1768
|
"percent_used": "{value}% 使用済み",
|
|
1757
1769
|
"reset_in": "{time}後にリセット",
|
|
1758
1770
|
"reset_imminent": "まもなくリセット",
|
|
@@ -2255,6 +2267,9 @@
|
|
|
2255
2267
|
"panel_world_cup": "월드컵 2026",
|
|
2256
2268
|
"panel_ai_daily": "AI 업데이트 일보",
|
|
2257
2269
|
"switch_panel": "패널 전환",
|
|
2270
|
+
"settings_button": "⚙ 설정",
|
|
2271
|
+
"statusline_sync_menu": "상태 표시줄로 사용량 동기화",
|
|
2272
|
+
"statusline_sync_tooltip": "Claude Code 상태 표시줄에서 사용량을 읽습니다. 끄면 Claude 사용량이 더 이상 업데이트되지 않습니다.",
|
|
2258
2273
|
"launch_at_login": "로그인 시 시작",
|
|
2259
2274
|
"reset_panel_position": "패널 위치 재설정",
|
|
2260
2275
|
"hide_sections_menu": "섹션 숨기기",
|
|
@@ -2310,8 +2325,8 @@
|
|
|
2310
2325
|
"service_degraded": "성능 저하",
|
|
2311
2326
|
"service_partial_outage": "부분 장애",
|
|
2312
2327
|
"service_major_outage": "대규모 장애",
|
|
2313
|
-
"
|
|
2314
|
-
"
|
|
2328
|
+
"today_label": "오늘:",
|
|
2329
|
+
"yesterday_label": "어제:",
|
|
2315
2330
|
"percent_used": "{value}% 사용됨",
|
|
2316
2331
|
"reset_in": "{time} 후 초기화",
|
|
2317
2332
|
"reset_imminent": "곧 초기화",
|
|
@@ -20,6 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import TypedDict, TypeGuard
|
|
21
21
|
|
|
22
22
|
from loaders import muse_loader
|
|
23
|
+
from usage_common.subprocess_utils import hidden_console_kwargs
|
|
23
24
|
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
25
26
|
CACHE_PATH = Path.home() / ".usage/muse_quota.json"
|
|
@@ -203,6 +204,7 @@ def _probe_worker(executable: str) -> None:
|
|
|
203
204
|
stderr=subprocess.PIPE,
|
|
204
205
|
encoding="utf-8",
|
|
205
206
|
cwd=PROBE_DIR,
|
|
207
|
+
**hidden_console_kwargs(),
|
|
206
208
|
)
|
|
207
209
|
_probe_process = process
|
|
208
210
|
try:
|
|
@@ -138,10 +138,7 @@ from menubar.state import (
|
|
|
138
138
|
_group_name as _group_name,
|
|
139
139
|
)
|
|
140
140
|
from menubar.state import (
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
from menubar.state import (
|
|
144
|
-
_today_title as _today_title,
|
|
141
|
+
_today_total as _today_total,
|
|
145
142
|
)
|
|
146
143
|
from menubar.state import (
|
|
147
144
|
format_human_time as format_human_time,
|
|
@@ -924,7 +921,6 @@ class AppDelegate(NSObject):
|
|
|
924
921
|
|
|
925
922
|
def _finishStatuslineAction_(self, result: dict[str, Any]) -> None:
|
|
926
923
|
self._refresh()
|
|
927
|
-
self._refresh_statusline_state()
|
|
928
924
|
if result.get("ok", True):
|
|
929
925
|
return
|
|
930
926
|
alert = _make_alert()
|
|
@@ -932,10 +928,6 @@ class AppDelegate(NSObject):
|
|
|
932
928
|
alert.setInformativeText_(str(result.get("output") or result.get("action") or ""))
|
|
933
929
|
alert.runModal()
|
|
934
930
|
|
|
935
|
-
def _refresh_statusline_state(self) -> None:
|
|
936
|
-
self.latest_state.statusline = _statusline_payload(self.language)
|
|
937
|
-
self.popover_controller.setState_(self.latest_state)
|
|
938
|
-
|
|
939
931
|
def _analyze_usage_in_background(self, period: str) -> None:
|
|
940
932
|
menubar_actions.analyze_usage_in_background(self, period)
|
|
941
933
|
|
|
@@ -11,6 +11,7 @@ from typing import Any, Protocol
|
|
|
11
11
|
from AppKit import NSMakePoint, NSMenu, NSMenuItem
|
|
12
12
|
|
|
13
13
|
from installer import claude_pane, login_item, session_hooks
|
|
14
|
+
from installer.statusline_settings import _statusline_enabled
|
|
14
15
|
from menubar.new_badge import apply_badge
|
|
15
16
|
from menubar.prefs import (
|
|
16
17
|
_hide_agy_enabled,
|
|
@@ -205,6 +206,16 @@ def build_switch_menu(app: _SwitchMenuApp, sender: Any) -> None:
|
|
|
205
206
|
menu.addItem_(NSMenuItem.separatorItem())
|
|
206
207
|
# Claude Code-only switches stay collapsed in a submenu, like the panel themes.
|
|
207
208
|
claude_submenu = NSMenu.alloc().initWithTitle_(_t(app.language, "claude_code_section"))
|
|
209
|
+
claude_submenu.addItem_(
|
|
210
|
+
build_menu_item(
|
|
211
|
+
app.language,
|
|
212
|
+
"statusline_sync_menu",
|
|
213
|
+
"toggleStatusline:",
|
|
214
|
+
target=app,
|
|
215
|
+
state=_statusline_enabled(),
|
|
216
|
+
tooltip_key="statusline_sync_tooltip",
|
|
217
|
+
)
|
|
218
|
+
)
|
|
208
219
|
claude_pane_item = build_menu_item(
|
|
209
220
|
app.language,
|
|
210
221
|
"claude_pane_section_menu",
|
|
@@ -140,9 +140,8 @@ def build_result(app: _RefreshApp, sources: RefreshSources) -> dict[str, Any]:
|
|
|
140
140
|
project_rows_7d = list(fallback_state.projects_7d)
|
|
141
141
|
project_rows_30d = list(fallback_state.projects_30d)
|
|
142
142
|
project_rows_all = list(fallback_state.projects_all)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
statusline = fallback_state.statusline
|
|
143
|
+
today_total = fallback_state.today_total
|
|
144
|
+
yesterday_total = fallback_state.yesterday_total
|
|
146
145
|
hide_claude = fallback_state.hide_claude
|
|
147
146
|
hide_codex = fallback_state.hide_codex
|
|
148
147
|
hide_agy = agy_result.hide_agy or _hide_agy_enabled()
|
|
@@ -171,9 +170,8 @@ def build_result(app: _RefreshApp, sources: RefreshSources) -> dict[str, Any]:
|
|
|
171
170
|
project_rows_all,
|
|
172
171
|
) = menubar_state.project_rows_for_windows(all_entries)
|
|
173
172
|
sources.measure("project_rows_windows", started_at)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
statusline = menubar_state._statusline_payload(app.language)
|
|
173
|
+
today_total = menubar_state._today_total(app.mock, entries=all_entries)
|
|
174
|
+
yesterday_total = menubar_state._yesterday_total(app.mock, entries=all_entries)
|
|
177
175
|
hide_claude = _hide_claude_enabled()
|
|
178
176
|
hide_codex = _hide_codex_enabled()
|
|
179
177
|
hide_agy = agy_result.hide_agy or _hide_agy_enabled()
|
|
@@ -219,9 +217,8 @@ def build_result(app: _RefreshApp, sources: RefreshSources) -> dict[str, Any]:
|
|
|
219
217
|
language=app.language,
|
|
220
218
|
group=group,
|
|
221
219
|
burn_rate_trackers=app.burn_rate_trackers,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
statusline=statusline,
|
|
220
|
+
today_total=today_total,
|
|
221
|
+
yesterday_total=yesterday_total,
|
|
225
222
|
show_install_button=show_install_button,
|
|
226
223
|
hide_claude=hide_claude,
|
|
227
224
|
hide_codex=hide_codex,
|
|
@@ -283,9 +280,8 @@ def build_result(app: _RefreshApp, sources: RefreshSources) -> dict[str, Any]:
|
|
|
283
280
|
state.projects_7d = project_rows_7d
|
|
284
281
|
state.projects_30d = project_rows_30d
|
|
285
282
|
state.projects_all = project_rows_all
|
|
286
|
-
state.
|
|
287
|
-
state.
|
|
288
|
-
state.statusline = statusline
|
|
283
|
+
state.today_total = today_total
|
|
284
|
+
state.yesterday_total = yesterday_total
|
|
289
285
|
state.hide_claude = hide_claude
|
|
290
286
|
state.hide_codex = hide_codex
|
|
291
287
|
state.hide_agy = hide_agy
|
|
@@ -16,7 +16,6 @@ from datetime import time as datetime_time
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import TYPE_CHECKING, Any, NotRequired, Protocol, TypedDict, cast
|
|
18
18
|
|
|
19
|
-
from installer.statusline_settings import _statusline_enabled
|
|
20
19
|
from loaders import codex_loader, grok_loader, muse_loader
|
|
21
20
|
from loaders.claude_paths import claude_config_dirs
|
|
22
21
|
from loaders.claude_usage import PollOutcome, PollState
|
|
@@ -90,6 +89,12 @@ def _bar_color(pct: float, brand: tuple[float, float, float]) -> tuple[float, fl
|
|
|
90
89
|
return brand
|
|
91
90
|
|
|
92
91
|
|
|
92
|
+
@dataclass(slots=True, frozen=True)
|
|
93
|
+
class PeriodTotal:
|
|
94
|
+
tokens: int = 0
|
|
95
|
+
cost: float = 0.0
|
|
96
|
+
|
|
97
|
+
|
|
93
98
|
@dataclass(slots=True)
|
|
94
99
|
class QuotaRowState:
|
|
95
100
|
title: str
|
|
@@ -155,9 +160,8 @@ class PopoverState:
|
|
|
155
160
|
projects_all: list[tuple[str, int, float | None]]
|
|
156
161
|
rate_text: str
|
|
157
162
|
status_text: str
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
statusline: dict[str, object]
|
|
163
|
+
today_total: PeriodTotal
|
|
164
|
+
yesterday_total: PeriodTotal
|
|
161
165
|
service_alerts: tuple[str, ...] = ()
|
|
162
166
|
show_install_button: bool = False
|
|
163
167
|
status_long: bool = False
|
|
@@ -855,9 +859,8 @@ def build_popover_state(
|
|
|
855
859
|
language: str,
|
|
856
860
|
group: int,
|
|
857
861
|
burn_rate_trackers: dict[str, BurnRateTracker],
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
statusline: dict[str, object],
|
|
862
|
+
today_total: PeriodTotal,
|
|
863
|
+
yesterday_total: PeriodTotal,
|
|
861
864
|
show_install_button: bool,
|
|
862
865
|
hide_claude: bool,
|
|
863
866
|
hide_codex: bool,
|
|
@@ -988,9 +991,8 @@ def build_popover_state(
|
|
|
988
991
|
projects_all=projects_all,
|
|
989
992
|
rate_text=_t(language, "rate_text", value=group_name),
|
|
990
993
|
status_text=status_text,
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
statusline=statusline,
|
|
994
|
+
today_total=today_total,
|
|
995
|
+
yesterday_total=yesterday_total,
|
|
994
996
|
service_alerts=service_alerts,
|
|
995
997
|
show_install_button=show_install_button,
|
|
996
998
|
status_long=status_long,
|
|
@@ -1107,9 +1109,8 @@ def _missing_row(
|
|
|
1107
1109
|
|
|
1108
1110
|
|
|
1109
1111
|
def _format_percent(value: float) -> str:
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
return f"{value:.1f}"
|
|
1112
|
+
# Whole numbers only, rounded down so 99.7% never reads as an exhausted 100%.
|
|
1113
|
+
return str(int(value))
|
|
1113
1114
|
|
|
1114
1115
|
|
|
1115
1116
|
def popover_dimensions(state: PopoverState, panel: UsagePanel | None = None) -> tuple[float, float]:
|
|
@@ -1222,9 +1223,8 @@ def _empty_state(language: str = "en") -> PopoverState:
|
|
|
1222
1223
|
projects_all=[],
|
|
1223
1224
|
rate_text=_t(language, "rate_text", value="--"),
|
|
1224
1225
|
status_text=_t(language, "status_text", value=_t(language, "status_loading")),
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
statusline=_statusline_payload(language),
|
|
1226
|
+
today_total=PeriodTotal(),
|
|
1227
|
+
yesterday_total=PeriodTotal(),
|
|
1228
1228
|
service_alerts=(),
|
|
1229
1229
|
show_install_button=False,
|
|
1230
1230
|
hide_claude=_hide_claude_enabled(),
|
|
@@ -1248,27 +1248,18 @@ def _error_state(message: str, mock: bool, language: str = "en") -> PopoverState
|
|
|
1248
1248
|
"status_text",
|
|
1249
1249
|
value=_t(language, "status_error", message=message),
|
|
1250
1250
|
)
|
|
1251
|
-
state.
|
|
1252
|
-
state.
|
|
1251
|
+
state.today_total = _today_total(mock)
|
|
1252
|
+
state.yesterday_total = _yesterday_total(mock)
|
|
1253
1253
|
state.show_install_button = False
|
|
1254
1254
|
return state
|
|
1255
1255
|
|
|
1256
1256
|
|
|
1257
|
-
def
|
|
1258
|
-
return {
|
|
1259
|
-
"enabled": _statusline_enabled(),
|
|
1260
|
-
"enabledText": _t(language, "cli_enabled"),
|
|
1261
|
-
"disabledText": _t(language, "cli_disabled"),
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
def _today_title(
|
|
1257
|
+
def _today_total(
|
|
1266
1258
|
mock: bool = False,
|
|
1267
|
-
language: str = "en",
|
|
1268
1259
|
entries: list[UsageEntry] | None = None,
|
|
1269
|
-
) ->
|
|
1260
|
+
) -> PeriodTotal:
|
|
1270
1261
|
if mock:
|
|
1271
|
-
return
|
|
1262
|
+
return PeriodTotal(tokens=50_193_442, cost=45.20)
|
|
1272
1263
|
|
|
1273
1264
|
try:
|
|
1274
1265
|
today = datetime.now().astimezone().date()
|
|
@@ -1290,18 +1281,17 @@ def _today_title(
|
|
|
1290
1281
|
except Exception:
|
|
1291
1282
|
if os.environ.get("USAGE_DEBUG") == "1":
|
|
1292
1283
|
logger.warning("today totals load failed", exc_info=True)
|
|
1293
|
-
return
|
|
1284
|
+
return PeriodTotal()
|
|
1294
1285
|
|
|
1295
|
-
return
|
|
1286
|
+
return PeriodTotal(tokens=total_tokens, cost=total_cost)
|
|
1296
1287
|
|
|
1297
1288
|
|
|
1298
|
-
def
|
|
1289
|
+
def _yesterday_total(
|
|
1299
1290
|
mock: bool = False,
|
|
1300
|
-
language: str = "en",
|
|
1301
1291
|
entries: list[UsageEntry] | None = None,
|
|
1302
|
-
) ->
|
|
1292
|
+
) -> PeriodTotal:
|
|
1303
1293
|
if mock:
|
|
1304
|
-
return
|
|
1294
|
+
return PeriodTotal(tokens=48_200_000, cost=41.10)
|
|
1305
1295
|
|
|
1306
1296
|
try:
|
|
1307
1297
|
yesterday = datetime.now().astimezone().date() - timedelta(days=1)
|
|
@@ -1318,11 +1308,6 @@ def _yesterday_title(
|
|
|
1318
1308
|
except Exception:
|
|
1319
1309
|
if os.environ.get("USAGE_DEBUG") == "1":
|
|
1320
1310
|
logger.warning("yesterday totals load failed", exc_info=True)
|
|
1321
|
-
return
|
|
1311
|
+
return PeriodTotal()
|
|
1322
1312
|
|
|
1323
|
-
return
|
|
1324
|
-
language,
|
|
1325
|
-
"yesterday_text",
|
|
1326
|
-
cost=f"{total_cost:.2f}",
|
|
1327
|
-
tokens=f"{total_tokens:,}",
|
|
1328
|
-
)
|
|
1313
|
+
return PeriodTotal(tokens=total_tokens, cost=total_cost)
|
|
@@ -14,7 +14,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
14
14
|
from usage_common.i18n import _load_i18n_bundle
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
|
-
from menubar.state import PopoverState, QuotaRowState
|
|
17
|
+
from menubar.state import PeriodTotal, PopoverState, QuotaRowState
|
|
18
18
|
|
|
19
19
|
CORE_SCRIPT_FILENAME = "panels/panel_core.js"
|
|
20
20
|
|
|
@@ -155,12 +155,11 @@ def _state_payload(
|
|
|
155
155
|
"hideMuse": state.hide_muse,
|
|
156
156
|
"cardOrder": list(state.card_order),
|
|
157
157
|
"historyError": state.history_error,
|
|
158
|
-
"statusline": state.statusline,
|
|
159
158
|
"footer": {
|
|
160
159
|
"rate": state.rate_text,
|
|
161
160
|
"status": state.status_text,
|
|
162
|
-
"today": state.
|
|
163
|
-
"yesterday": state.
|
|
161
|
+
"today": _period_payload(state.today_total),
|
|
162
|
+
"yesterday": _period_payload(state.yesterday_total),
|
|
164
163
|
"serviceAlerts": list(state.service_alerts),
|
|
165
164
|
"showInstall": state.show_install_button,
|
|
166
165
|
},
|
|
@@ -176,3 +175,7 @@ def _fmt_tokens(tokens: int) -> str:
|
|
|
176
175
|
|
|
177
176
|
def _fmt_cost(cost: float | None) -> str:
|
|
178
177
|
return "--" if cost is None else f"${cost:.2f}"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _period_payload(total: PeriodTotal) -> dict[str, str]:
|
|
181
|
+
return {"tokensText": _fmt_tokens(total.tokens), "costText": _fmt_cost(total.cost)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "usage-cli"
|
|
3
|
-
version = "0.34.
|
|
3
|
+
version = "0.34.2"
|
|
4
4
|
description = "usage — 在 macOS menu bar 與 Windows 系統匣顯示 Claude Code、Codex、Antigravity 用量的小工具(也提供終端機 TUI)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
[project.optional-dependencies]
|
|
19
19
|
windows = [
|
|
20
20
|
"pystray>=0.19.5; sys_platform == 'win32'",
|
|
21
|
-
"pillow>=
|
|
21
|
+
"pillow>=13.0.0; sys_platform == 'win32'",
|
|
22
22
|
"pywebview>=5.4; sys_platform == 'win32'",
|
|
23
23
|
"windows-toasts>=1.3.1; sys_platform == 'win32'",
|
|
24
24
|
]
|
|
@@ -275,7 +275,7 @@ def test_html_panels_expose_analyze_action() -> None:
|
|
|
275
275
|
assert 'data-i18n="analyze_usage"' in html, path.name
|
|
276
276
|
assert "analyze_all" not in html, path.name
|
|
277
277
|
assert "projectsAll" in html, path.name
|
|
278
|
-
assert 'data-action="toggle-statusline"' in html, path.name
|
|
278
|
+
assert 'data-action="toggle-statusline"' not in html, path.name
|
|
279
279
|
|
|
280
280
|
|
|
281
281
|
def test_generate_analysis_report_uses_analyzer_pipeline(
|
|
@@ -84,7 +84,9 @@ const context = vm.createContext({
|
|
|
84
84
|
document: {querySelector: selector => selector.includes('[data-row=')
|
|
85
85
|
? rowEl : hasCards ? {} : null},
|
|
86
86
|
getComputedStyle: () => ({getPropertyValue: () => 'red'}),
|
|
87
|
-
window: {PanelHooks: {
|
|
87
|
+
window: {PanelHooks: {
|
|
88
|
+
accentFallback: () => 'red', percentColor: (accent) => accent, applyFillVisual() {},
|
|
89
|
+
}},
|
|
88
90
|
colorFor: () => 'red', labels: () => ({session: 'Session'}), t: key => key,
|
|
89
91
|
});
|
|
90
92
|
const toggle = 'function toggle(state) { const visibleQuotaCards =' + process.argv[1] + '}';
|
|
@@ -170,9 +170,8 @@ def test_desktop_quota_reaches_popover_and_tui(quota_file: Path) -> None:
|
|
|
170
170
|
burn_rate_trackers={
|
|
171
171
|
name: BurnRateTracker() for name in ("claude_session", "claude_weekly")
|
|
172
172
|
},
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
statusline={},
|
|
173
|
+
today_total=state_module.PeriodTotal(),
|
|
174
|
+
yesterday_total=state_module.PeriodTotal(),
|
|
176
175
|
show_install_button=False,
|
|
177
176
|
hide_claude=False,
|
|
178
177
|
hide_codex=False,
|
|
@@ -180,9 +180,8 @@ def _build_popover_state(
|
|
|
180
180
|
language=delegate.language,
|
|
181
181
|
group=delegate.tracker.group(),
|
|
182
182
|
burn_rate_trackers=delegate.burn_rate_trackers,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
statusline=menubar._statusline_payload(delegate.language),
|
|
183
|
+
today_total=menubar._today_total(delegate.mock),
|
|
184
|
+
yesterday_total=menubar_state._yesterday_total(delegate.mock),
|
|
186
185
|
show_install_button=(
|
|
187
186
|
not hide_claude
|
|
188
187
|
and outcome.state == PollState.TOKEN_ERROR
|
|
@@ -241,7 +240,8 @@ def test_format_human_time_minutes_hours_and_days() -> None:
|
|
|
241
240
|
|
|
242
241
|
def test_format_percent() -> None:
|
|
243
242
|
assert menubar._format_percent(50.0) == "50"
|
|
244
|
-
assert menubar._format_percent(50.5) == "50
|
|
243
|
+
assert menubar._format_percent(50.5) == "50"
|
|
244
|
+
assert menubar._format_percent(99.7) == "99"
|
|
245
245
|
assert menubar._format_percent(0.0) == "0"
|
|
246
246
|
|
|
247
247
|
|
|
@@ -310,7 +310,7 @@ def test_quota_row_formats_available_row() -> None:
|
|
|
310
310
|
|
|
311
311
|
assert row.available is True
|
|
312
312
|
assert row.percent == 50.5
|
|
313
|
-
assert row.percent_text == "50
|
|
313
|
+
assert row.percent_text == "50% 已用"
|
|
314
314
|
assert row.reset_text.startswith("重置 ")
|
|
315
315
|
assert row.warning is False
|
|
316
316
|
assert row.color == menubar.WARN_COLOR
|
|
@@ -548,11 +548,13 @@ def test_quota_row_keeps_reset_text_when_percent_is_below_warning_floor() -> Non
|
|
|
548
548
|
assert row.reset_text == "重置 51分鐘"
|
|
549
549
|
|
|
550
550
|
|
|
551
|
-
def
|
|
552
|
-
assert menubar.
|
|
551
|
+
def test_today_total_mock() -> None:
|
|
552
|
+
assert menubar._today_total(mock=True) == menubar_state.PeriodTotal(
|
|
553
|
+
tokens=50_193_442, cost=45.20
|
|
554
|
+
)
|
|
553
555
|
|
|
554
556
|
|
|
555
|
-
def
|
|
557
|
+
def test_today_total_returns_zero_fallback_when_loaders_fail(
|
|
556
558
|
monkeypatch: pytest.MonkeyPatch,
|
|
557
559
|
) -> None:
|
|
558
560
|
monkeypatch.setattr(
|
|
@@ -561,10 +563,10 @@ def test_today_title_returns_zero_fallback_when_loaders_fail(
|
|
|
561
563
|
lambda *, hours_back=24: (_ for _ in ()).throw(OSError),
|
|
562
564
|
)
|
|
563
565
|
|
|
564
|
-
assert menubar.
|
|
566
|
+
assert menubar._today_total(mock=False) == menubar_state.PeriodTotal()
|
|
565
567
|
|
|
566
568
|
|
|
567
|
-
def
|
|
569
|
+
def test_today_total_does_not_reload_codex_when_entries_are_provided(
|
|
568
570
|
monkeypatch: pytest.MonkeyPatch,
|
|
569
571
|
) -> None:
|
|
570
572
|
entry = history_loader.UsageEntry(
|
|
@@ -585,12 +587,12 @@ def test_today_title_does_not_reload_codex_when_entries_are_provided(
|
|
|
585
587
|
lambda *, hours_back=24: pytest.fail("Codex should already be included"),
|
|
586
588
|
)
|
|
587
589
|
|
|
588
|
-
assert menubar.
|
|
589
|
-
|
|
590
|
+
assert menubar._today_total(mock=False, entries=[entry]) == menubar_state.PeriodTotal(
|
|
591
|
+
tokens=150, cost=0.01
|
|
590
592
|
)
|
|
591
593
|
|
|
592
594
|
|
|
593
|
-
def
|
|
595
|
+
def test_yesterday_total_uses_provided_entries() -> None:
|
|
594
596
|
entry = history_loader.UsageEntry(
|
|
595
597
|
timestamp=datetime.now(tz=UTC) - timedelta(days=1),
|
|
596
598
|
session_id="codex",
|
|
@@ -605,13 +607,13 @@ def test_yesterday_title_uses_provided_entries() -> None:
|
|
|
605
607
|
project="usage",
|
|
606
608
|
)
|
|
607
609
|
|
|
608
|
-
assert menubar_state.
|
|
609
|
-
|
|
610
|
+
assert menubar_state._yesterday_total(False, entries=[entry]) == menubar_state.PeriodTotal(
|
|
611
|
+
tokens=150, cost=0.01
|
|
610
612
|
)
|
|
611
613
|
|
|
612
614
|
|
|
613
615
|
@pytest.mark.skipif(not hasattr(time, "tzset"), reason="requires POSIX timezone control")
|
|
614
|
-
def
|
|
616
|
+
def test_yesterday_total_uses_dst_specific_day_boundaries(
|
|
615
617
|
monkeypatch: pytest.MonkeyPatch,
|
|
616
618
|
) -> None:
|
|
617
619
|
class FixedDateTime(datetime):
|
|
@@ -647,8 +649,8 @@ def test_yesterday_title_uses_dst_specific_day_boundaries(
|
|
|
647
649
|
entry(datetime(2026, 3, 9, 4, 0, tzinfo=UTC)),
|
|
648
650
|
]
|
|
649
651
|
|
|
650
|
-
assert menubar_state.
|
|
651
|
-
|
|
652
|
+
assert menubar_state._yesterday_total(False, entries=entries) == (
|
|
653
|
+
menubar_state.PeriodTotal(tokens=10, cost=0.01)
|
|
652
654
|
)
|
|
653
655
|
finally:
|
|
654
656
|
if previous_tz is None:
|
|
@@ -673,9 +675,8 @@ def test_empty_state() -> None:
|
|
|
673
675
|
assert state.projects_7d == []
|
|
674
676
|
assert state.projects_30d == []
|
|
675
677
|
assert state.projects_all == []
|
|
676
|
-
assert isinstance(state.statusline["enabled"], bool)
|
|
677
678
|
assert state.show_install_button is False
|
|
678
|
-
assert state.
|
|
679
|
+
assert state.yesterday_total == menubar_state.PeriodTotal()
|
|
679
680
|
|
|
680
681
|
|
|
681
682
|
@pytest.mark.parametrize("quota_enabled", [True, False])
|
|
@@ -760,11 +761,14 @@ def test_switch_panel_menu_contains_update_items(
|
|
|
760
761
|
]
|
|
761
762
|
assert settings[7].submenu is claude_submenu
|
|
762
763
|
assert [item.title for item in claude_submenu.items] == [
|
|
764
|
+
"Sync quota via status line",
|
|
763
765
|
"Side pane",
|
|
764
766
|
"Beginner Mode",
|
|
765
767
|
"Quota-Aware Mode",
|
|
766
768
|
]
|
|
767
|
-
pane, beginner, quota = claude_submenu.items
|
|
769
|
+
statusline, pane, beginner, quota = claude_submenu.items
|
|
770
|
+
assert statusline.action == "toggleStatusline:"
|
|
771
|
+
assert statusline.tooltip == _t("en", "statusline_sync_tooltip")
|
|
768
772
|
assert pane.title == _t("en", "claude_pane_section_menu")
|
|
769
773
|
assert pane.action == "toggleClaudePane:"
|
|
770
774
|
assert pane.tooltip == _t("en", "claude_pane_tooltip")
|
|
@@ -1415,11 +1419,11 @@ def test_statusline_switch_survives_agy_failure(
|
|
|
1415
1419
|
assert menubar._enable_statusline_settings() == 0
|
|
1416
1420
|
|
|
1417
1421
|
|
|
1418
|
-
def
|
|
1422
|
+
def test_error_state_uses_message_and_mock_today_total() -> None:
|
|
1419
1423
|
state = menubar._error_state("boom", mock=True, language="zh-TW")
|
|
1420
1424
|
|
|
1421
1425
|
assert "boom" in state.status_text
|
|
1422
|
-
assert state.
|
|
1426
|
+
assert state.today_total == menubar_state.PeriodTotal(tokens=50_193_442, cost=45.20)
|
|
1423
1427
|
|
|
1424
1428
|
|
|
1425
1429
|
def test_popover_size_has_positive_dimensions() -> None:
|
|
@@ -2447,7 +2451,7 @@ def test_apply_codex_refresh_result_updates_quota_before_full_refresh(
|
|
|
2447
2451
|
assert delegate.codex_5h_pct == 18.5
|
|
2448
2452
|
assert delegate.codex_model == "gpt-test"
|
|
2449
2453
|
assert controller.calls == [delegate.latest_state]
|
|
2450
|
-
assert button.titles[-1].endswith("18
|
|
2454
|
+
assert button.titles[-1].endswith("18%")
|
|
2451
2455
|
|
|
2452
2456
|
|
|
2453
2457
|
def test_refresh_error_preserves_codex_quota(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -2601,7 +2605,7 @@ def test_refresh_error_preserves_project_usage(monkeypatch: pytest.MonkeyPatch)
|
|
|
2601
2605
|
assert state.projects_7d == [("Eric-Tools", 165, 0.01)]
|
|
2602
2606
|
assert state.projects_30d == [("Eric-Tools", 165, 0.01)]
|
|
2603
2607
|
assert state.projects_all == [("Eric-Tools", 165, 0.01)]
|
|
2604
|
-
assert
|
|
2608
|
+
assert state.today_total.tokens == 165
|
|
2605
2609
|
|
|
2606
2610
|
|
|
2607
2611
|
def test_refresh_success_builds_popover_state_and_pings_window_keeper(
|
|
@@ -2653,11 +2657,6 @@ def test_refresh_success_builds_popover_state_and_pings_window_keeper(
|
|
|
2653
2657
|
lambda reset_at, percent, source, mock: pings.append((reset_at, percent, source, mock)),
|
|
2654
2658
|
)
|
|
2655
2659
|
monkeypatch.setattr(agy_window_keeper, "maybe_ping", lambda *args: None)
|
|
2656
|
-
monkeypatch.setattr(
|
|
2657
|
-
menubar_state,
|
|
2658
|
-
"_statusline_payload",
|
|
2659
|
-
lambda language: {"enabled": False, "language": language},
|
|
2660
|
-
)
|
|
2661
2660
|
|
|
2662
2661
|
class Delegate:
|
|
2663
2662
|
mock = False
|
|
@@ -3378,7 +3377,6 @@ def test_refresh_preserves_muse_on_success_and_fetch_error(
|
|
|
3378
3377
|
"get_service_status",
|
|
3379
3378
|
lambda config: ServiceStatus(config.service_name, False, "operational", "", "cache"),
|
|
3380
3379
|
)
|
|
3381
|
-
monkeypatch.setattr(state, "_statusline_payload", lambda _language: {})
|
|
3382
3380
|
monkeypatch.setattr("quota.codex_window_keeper.maybe_ping", lambda _mock: None)
|
|
3383
3381
|
monkeypatch.setattr("quota.agy_window_keeper.maybe_ping", lambda *_args: None)
|
|
3384
3382
|
|