usage-cli 0.34.5__tar.gz → 0.34.6__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.
Files changed (282) hide show
  1. {usage_cli-0.34.5/usage_cli.egg-info → usage_cli-0.34.6}/PKG-INFO +1 -1
  2. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/dispatch_ledger.py +113 -27
  3. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/reporter.py +13 -2
  4. {usage_cli-0.34.5 → usage_cli-0.34.6}/i18n.json +55 -75
  5. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/muse_quota_probe.py +19 -9
  6. {usage_cli-0.34.5 → usage_cli-0.34.6}/pyproject.toml +1 -1
  7. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_dispatch_ledger.py +244 -80
  8. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_muse_quota_probe.py +29 -3
  9. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_report_cold_read.py +10 -19
  10. usage_cli-0.34.6/ui/report_ledger.py +102 -0
  11. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/report_styles.py +4 -0
  12. {usage_cli-0.34.5 → usage_cli-0.34.6/usage_cli.egg-info}/PKG-INFO +1 -1
  13. usage_cli-0.34.5/ui/report_ledger.py +0 -88
  14. {usage_cli-0.34.5 → usage_cli-0.34.6}/LICENSE +0 -0
  15. {usage_cli-0.34.5 → usage_cli-0.34.6}/README.md +0 -0
  16. {usage_cli-0.34.5 → usage_cli-0.34.6}/THIRD_PARTY_NOTICES.md +0 -0
  17. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/__init__.py +0 -0
  18. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/agy.py +0 -0
  19. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/claude.py +0 -0
  20. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/codex.py +0 -0
  21. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/grok.py +0 -0
  22. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/muse.py +0 -0
  23. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/rate_limits.py +0 -0
  24. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/registry.py +0 -0
  25. {usage_cli-0.34.5 → usage_cli-0.34.6}/adapters/types.py +0 -0
  26. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/__init__.py +0 -0
  27. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/aggregator.py +0 -0
  28. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/blocks.py +0 -0
  29. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/diagnoser.py +0 -0
  30. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/diagnosis_snapshot.py +0 -0
  31. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/history_persist.py +0 -0
  32. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/insights.py +0 -0
  33. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/ledger_backfill.py +0 -0
  34. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/persona_loader.py +0 -0
  35. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/subscription.py +0 -0
  36. {usage_cli-0.34.5 → usage_cli-0.34.6}/analyzer/usage_snapshot.py +0 -0
  37. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/__init__.py +0 -0
  38. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/claude_pane.py +0 -0
  39. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/codex_hook_trust.py +0 -0
  40. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/login_item.py +0 -0
  41. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/session_hooks.py +0 -0
  42. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/setup_hook.py +0 -0
  43. {usage_cli-0.34.5 → usage_cli-0.34.6}/installer/statusline_settings.py +0 -0
  44. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/__init__.py +0 -0
  45. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/_shared_cache_file.py +0 -0
  46. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/agy_disk_cache.py +0 -0
  47. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/agy_loader.py +0 -0
  48. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/agy_quota_probe.py +0 -0
  49. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/cache_quarantine.py +0 -0
  50. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/chromium_cache.py +0 -0
  51. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/claude_desktop.py +0 -0
  52. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/claude_paths.py +0 -0
  53. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/claude_usage.py +0 -0
  54. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/codex_disk_cache.py +0 -0
  55. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/codex_events.py +0 -0
  56. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/codex_fork_replay.py +0 -0
  57. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/codex_loader.py +0 -0
  58. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/codex_paths.py +0 -0
  59. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/disk_cache_common.py +0 -0
  60. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/disk_cache_lifecycle.py +0 -0
  61. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/grok_loader.py +0 -0
  62. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/grok_quota_probe.py +0 -0
  63. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/history_disk_cache.py +0 -0
  64. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/history_loader.py +0 -0
  65. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/jsonl_limits.py +0 -0
  66. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/jsonl_utils.py +0 -0
  67. {usage_cli-0.34.5 → usage_cli-0.34.6}/loaders/muse_loader.py +0 -0
  68. {usage_cli-0.34.5 → usage_cli-0.34.6}/main.py +0 -0
  69. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/__init__.py +0 -0
  70. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/actions.py +0 -0
  71. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/agy.py +0 -0
  72. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/app.py +0 -0
  73. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/chrome.py +0 -0
  74. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/fsevents_watch.py +0 -0
  75. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/grok.py +0 -0
  76. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/manual_refresh.py +0 -0
  77. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/menu.py +0 -0
  78. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/muse.py +0 -0
  79. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/new_badge.py +0 -0
  80. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/notify.py +0 -0
  81. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/popover.py +0 -0
  82. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/prefs.py +0 -0
  83. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/refresh.py +0 -0
  84. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/state.py +0 -0
  85. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/title.py +0 -0
  86. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/update.py +0 -0
  87. {usage_cli-0.34.5 → usage_cli-0.34.6}/menubar/whats_new.py +0 -0
  88. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/__init__.py +0 -0
  89. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/base.py +0 -0
  90. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/catalog.py +0 -0
  91. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/dynamic_height.py +0 -0
  92. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/panel_scale.py +0 -0
  93. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/panel_window.py +0 -0
  94. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/panel_window_state.py +0 -0
  95. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/payload.py +0 -0
  96. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/web_panel.py +0 -0
  97. {usage_cli-0.34.5 → usage_cli-0.34.6}/panels/window_drag.py +0 -0
  98. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/__init__.py +0 -0
  99. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/agy_window_keeper.py +0 -0
  100. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/burn_rate.py +0 -0
  101. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/codex_window_keeper.py +0 -0
  102. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/keeper_outcome.py +0 -0
  103. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/notifications.py +0 -0
  104. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/quota_snapshot.py +0 -0
  105. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/status_payload.py +0 -0
  106. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/usage_rate.py +0 -0
  107. {usage_cli-0.34.5 → usage_cli-0.34.6}/quota/window_keeper.py +0 -0
  108. {usage_cli-0.34.5 → usage_cli-0.34.6}/setup.cfg +0 -0
  109. {usage_cli-0.34.5 → usage_cli-0.34.6}/setup_app.py +0 -0
  110. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_adapters_claude.py +0 -0
  111. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_adapters_codex.py +0 -0
  112. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_adapters_rate_limits.py +0 -0
  113. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_aggregator.py +0 -0
  114. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_agy_adapter.py +0 -0
  115. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_agy_disk_cache.py +0 -0
  116. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_agy_loader.py +0 -0
  117. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_agy_quota_probe.py +0 -0
  118. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_agy_window_keeper.py +0 -0
  119. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_analyzer_blocks.py +0 -0
  120. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_analyzer_pipeline.py +0 -0
  121. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_app_version.py +0 -0
  122. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_burn_rate.py +0 -0
  123. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_cache_quarantine.py +0 -0
  124. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_check_upstream.py +0 -0
  125. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_chromium_cache.py +0 -0
  126. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_claude_desktop.py +0 -0
  127. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_claude_pane.py +0 -0
  128. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_claude_paths.py +0 -0
  129. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_codex_consistency.py +0 -0
  130. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_codex_hook_trust.py +0 -0
  131. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_codex_loader.py +0 -0
  132. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_codex_window_keeper.py +0 -0
  133. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_compact_cards.py +0 -0
  134. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_desktop_fresh_cache.py +0 -0
  135. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_desktop_quota_display.py +0 -0
  136. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_desktop_quota_review.py +0 -0
  137. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_diagnoser.py +0 -0
  138. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_disk_cache_shards.py +0 -0
  139. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_doctor.py +0 -0
  140. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_dynamic_height.py +0 -0
  141. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_fsevents_watch.py +0 -0
  142. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_grok_loader.py +0 -0
  143. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_grok_quota_probe.py +0 -0
  144. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_history_loader.py +0 -0
  145. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_history_persist.py +0 -0
  146. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_hook_scripts_are_standalone.py +0 -0
  147. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_html_report_snapshot.py +0 -0
  148. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_i18n_format_fallback.py +0 -0
  149. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_i18n_key_parity.py +0 -0
  150. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_i18n_packaged_path.py +0 -0
  151. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_insights.py +0 -0
  152. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_jsonl_utils.py +0 -0
  153. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_keeper_menu.py +0 -0
  154. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_keeper_outcome.py +0 -0
  155. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_ledger_backfill.py +0 -0
  156. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_login_item.py +0 -0
  157. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_main.py +0 -0
  158. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_manual_refresh.py +0 -0
  159. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar.py +0 -0
  160. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_agy.py +0 -0
  161. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_grok.py +0 -0
  162. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_muse.py +0 -0
  163. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_notify.py +0 -0
  164. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_prefs.py +0 -0
  165. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_state.py +0 -0
  166. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_menubar_update.py +0 -0
  167. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_muse_coverage.py +0 -0
  168. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_muse_loader.py +0 -0
  169. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_muse_review_regressions.py +0 -0
  170. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_new_badge.py +0 -0
  171. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_packaged_resources.py +0 -0
  172. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_packaging.py +0 -0
  173. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_panel_footer_layout.py +0 -0
  174. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_panel_scale.py +0 -0
  175. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_panel_window_state.py +0 -0
  176. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_panels.py +0 -0
  177. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_panels_lazy_import.py +0 -0
  178. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_persona_loader.py +0 -0
  179. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_pricing.py +0 -0
  180. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_project_resolver.py +0 -0
  181. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_quota_aware.py +0 -0
  182. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_quota_label.py +0 -0
  183. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_report_daily_chart.py +0 -0
  184. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_report_filter_js.py +0 -0
  185. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_report_share_mask.py +0 -0
  186. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_reporter_build_data.py +0 -0
  187. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_service_status.py +0 -0
  188. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_session_resume.py +0 -0
  189. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_setup_app.py +0 -0
  190. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_setup_hook.py +0 -0
  191. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_setup_hook_coexistence.py +0 -0
  192. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_setup_hook_resume.py +0 -0
  193. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_setup_hook_terse.py +0 -0
  194. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_subprocess_utils.py +0 -0
  195. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_subscription.py +0 -0
  196. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_taskbar_menu.py +0 -0
  197. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_taskbar_obstacles.py +0 -0
  198. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_time_utils.py +0 -0
  199. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_tui.py +0 -0
  200. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_tui_sprite.py +0 -0
  201. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_ui_tables.py +0 -0
  202. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_update_checker.py +0 -0
  203. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_update_gate.py +0 -0
  204. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_update_release_notes.py +0 -0
  205. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_cli.py +0 -0
  206. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_client.py +0 -0
  207. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_diagnosis_snapshot.py +0 -0
  208. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_dir_sweeper.py +0 -0
  209. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_lang.py +0 -0
  210. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_logging.py +0 -0
  211. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_notifications.py +0 -0
  212. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_rate.py +0 -0
  213. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_snapshot.py +0 -0
  214. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_snapshot_replay.py +0 -0
  215. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_statusline.py +0 -0
  216. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_statusline_agy.py +0 -0
  217. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_statusline_forwarder.py +0 -0
  218. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_statusline_grok.py +0 -0
  219. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_terse_mode.py +0 -0
  220. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_usage_terse_reminder.py +0 -0
  221. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_web_panel_payload.py +0 -0
  222. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_whats_new.py +0 -0
  223. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_wheel_packaging.py +0 -0
  224. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_win_login_item.py +0 -0
  225. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_window_keeper.py +0 -0
  226. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_windows_manifest.py +0 -0
  227. {usage_cli-0.34.5 → usage_cli-0.34.6}/tests/test_wintray.py +0 -0
  228. {usage_cli-0.34.5 → usage_cli-0.34.6}/tui/__init__.py +0 -0
  229. {usage_cli-0.34.5 → usage_cli-0.34.6}/tui/app.py +0 -0
  230. {usage_cli-0.34.5 → usage_cli-0.34.6}/tui/sprite.py +0 -0
  231. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/__init__.py +0 -0
  232. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/html_report.py +0 -0
  233. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/report_charts.py +0 -0
  234. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/report_daily_chart.py +0 -0
  235. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/report_filter.py +0 -0
  236. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/report_scripts.py +0 -0
  237. {usage_cli-0.34.5 → usage_cli-0.34.6}/ui/tables.py +0 -0
  238. {usage_cli-0.34.5 → usage_cli-0.34.6}/updates/__init__.py +0 -0
  239. {usage_cli-0.34.5 → usage_cli-0.34.6}/updates/checker.py +0 -0
  240. {usage_cli-0.34.5 → usage_cli-0.34.6}/updates/gate.py +0 -0
  241. {usage_cli-0.34.5 → usage_cli-0.34.6}/updates/release_notes.py +0 -0
  242. {usage_cli-0.34.5 → usage_cli-0.34.6}/updates/whats_new.py +0 -0
  243. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_app/__init__.py +0 -0
  244. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_app/cli.py +0 -0
  245. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_app/doctor.py +0 -0
  246. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_cli.egg-info/SOURCES.txt +0 -0
  247. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_cli.egg-info/dependency_links.txt +0 -0
  248. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_cli.egg-info/entry_points.txt +0 -0
  249. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_cli.egg-info/requires.txt +0 -0
  250. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_cli.egg-info/top_level.txt +0 -0
  251. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/__init__.py +0 -0
  252. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/app_version.py +0 -0
  253. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/i18n.py +0 -0
  254. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/prefs.py +0 -0
  255. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/pricing.py +0 -0
  256. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/project_resolver.py +0 -0
  257. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/service_status.py +0 -0
  258. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/subprocess_utils.py +0 -0
  259. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/time_utils.py +0 -0
  260. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/usage_dir_sweeper.py +0 -0
  261. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/usage_lang.py +0 -0
  262. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_common/usage_logging.py +0 -0
  263. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/__init__.py +0 -0
  264. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_quota_aware.py +0 -0
  265. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_session_resume.py +0 -0
  266. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_statusline.py +0 -0
  267. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_statusline_agy.py +0 -0
  268. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_statusline_forwarder.py +0 -0
  269. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_statusline_grok.py +0 -0
  270. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_terse_mode.py +0 -0
  271. {usage_cli-0.34.5 → usage_cli-0.34.6}/usage_hooks/usage_terse_reminder.py +0 -0
  272. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/__init__.py +0 -0
  273. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/app.py +0 -0
  274. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/login_item.py +0 -0
  275. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/menu.py +0 -0
  276. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/prefs.py +0 -0
  277. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/quota_label.py +0 -0
  278. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/taskbar_menu.py +0 -0
  279. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/taskbar_obstacles.py +0 -0
  280. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/taskbar_overlay.py +0 -0
  281. {usage_cli-0.34.5 → usage_cli-0.34.6}/wintray/watch.py +0 -0
  282. {usage_cli-0.34.5 → usage_cli-0.34.6}/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.5
3
+ Version: 0.34.6
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
@@ -18,6 +18,7 @@ from adapters import rate_limits
18
18
  from adapters.types import UsageEntry
19
19
  from loaders import agy_loader, agy_quota_probe, codex_loader, muse_quota_probe
20
20
  from loaders.claude_paths import claude_config_dirs
21
+ from usage_common.project_resolver import resolve_project_name
21
22
 
22
23
  _DISPATCH = {
23
24
  "codex": re.compile(r"(^|[;&|(\s])codex exec"),
@@ -61,7 +62,10 @@ class LedgerRow(TypedDict):
61
62
  dispatch_count: int
62
63
  claude_pct: float | None
63
64
  codex_pct: float | None
64
- agy_pct: float | None
65
+ agy_groups: dict[str, float]
66
+ claude_reset: bool
67
+ codex_reset: bool
68
+ agy_reset: bool
65
69
  total_pct: float
66
70
 
67
71
 
@@ -69,6 +73,52 @@ class LedgerData(TypedDict):
69
73
  rows: list[LedgerRow]
70
74
  unowned_codex: int
71
75
  unowned_agy: int
76
+ used_pct: dict[str, float]
77
+
78
+
79
+ class LedgerBlock(TypedDict):
80
+ quota: str
81
+ used_pct: float | None
82
+ conversations: list[tuple[LedgerRow, float]]
83
+ more_count: int
84
+ more_pct: float
85
+ other_pct: float | None
86
+ all_reset: bool
87
+
88
+
89
+ def ledger_blocks(rows: list[LedgerRow], used_pct: dict[str, float]) -> list[LedgerBlock]:
90
+ """Select each quota's top five from every owned conversation."""
91
+ shares: dict[str, list[tuple[LedgerRow, float]]] = defaultdict(list)
92
+ for row in rows:
93
+ if row["codex_pct"] is not None and row["codex_pct"] > 0:
94
+ shares["codex"].append((row, row["codex_pct"]))
95
+ for group, value in row["agy_groups"].items():
96
+ if value > 0:
97
+ shares[f"antigravity:{group}"].append((row, value))
98
+ if any(row["codex_reset"] for row in rows):
99
+ shares.setdefault("codex", [])
100
+ blocks: list[LedgerBlock] = []
101
+ for quota, conversations in shares.items():
102
+ conversations.sort(key=lambda item: item[1], reverse=True)
103
+ total = sum(value for _, value in conversations)
104
+ used = used_pct.get(quota)
105
+ other = max(0.0, used - total) if used is not None else None
106
+ blocks.append(
107
+ {
108
+ "quota": quota,
109
+ "used_pct": used,
110
+ "conversations": conversations[:5],
111
+ "more_count": len(conversations[5:]),
112
+ "more_pct": sum(value for _, value in conversations[5:]),
113
+ "other_pct": other if other is not None and other >= 0.5 else None,
114
+ "all_reset": quota == "codex" and not conversations,
115
+ }
116
+ )
117
+ blocks.sort(
118
+ key=lambda block: sum(value for _, value in block["conversations"]) + block["more_pct"],
119
+ reverse=True,
120
+ )
121
+ return blocks
72
122
 
73
123
 
74
124
  def _timestamp(value: object) -> datetime | None:
@@ -319,11 +369,15 @@ def estimate_pct(
319
369
  return session_tokens / total_tokens * window.used_pct
320
370
 
321
371
 
372
+ SessionGroup = tuple[str, str, str]
373
+
374
+
322
375
  def estimate_sessions(
323
376
  entries: list[UsageEntry], windows: dict[str, QuotaWindow], now: datetime
324
- ) -> dict[tuple[str, str], float | None]:
377
+ ) -> tuple[dict[SessionGroup, float], set[SessionGroup]]:
325
378
  totals: dict[str, int] = defaultdict(int)
326
379
  sessions: dict[tuple[str, str, str], int] = defaultdict(int)
380
+ before_window: set[SessionGroup] = set()
327
381
  for entry in entries:
328
382
  group = entry.agent_id
329
383
  if entry.agent_id == "antigravity":
@@ -351,18 +405,21 @@ def estimate_sessions(
351
405
  "",
352
406
  )
353
407
  window = windows.get(group)
354
- if window is None or not window.resets_at - window.length <= entry.timestamp <= now:
355
- continue
356
408
  tokens = entry.input_tokens + entry.output_tokens + entry.cache_creation_tokens
409
+ if window is None or entry.timestamp > now or tokens <= 0:
410
+ continue
411
+ key = (entry.agent_id, entry.session_id, group)
412
+ if entry.timestamp < window.resets_at - window.length:
413
+ before_window.add(key)
414
+ continue
357
415
  totals[group] += tokens
358
- sessions[(entry.agent_id, entry.session_id, group)] += tokens
359
- estimates: dict[tuple[str, str], float | None] = {}
416
+ sessions[key] += tokens
417
+ estimates: dict[SessionGroup, float] = {}
360
418
  for (agent_id, session_id, group), tokens in sessions.items():
361
419
  value = estimate_pct(tokens, totals[group], windows.get(group), now)
362
420
  if value is not None:
363
- key = (agent_id, session_id)
364
- estimates[key] = (estimates.get(key) or 0.0) + value
365
- return estimates
421
+ estimates[(agent_id, session_id, group)] = value
422
+ return estimates, before_window - sessions.keys()
366
423
 
367
424
 
368
425
  def _session_title(path: Path) -> str:
@@ -392,13 +449,23 @@ def _session_title(path: Path) -> str:
392
449
  return title or first_text
393
450
 
394
451
 
395
- def build_ledger(estimates: dict[tuple[str, str], float | None], now: datetime) -> LedgerData:
452
+ def build_ledger(
453
+ estimates: dict[SessionGroup, float],
454
+ now: datetime,
455
+ reset_sessions: set[SessionGroup] | None = None,
456
+ used_pct: dict[str, float] | None = None,
457
+ ) -> LedgerData:
396
458
  cutoff = now - timedelta(days=7)
397
459
  paths = _claude_paths(cutoff)
398
460
  dispatches = [
399
461
  item for path in paths for item in _read_dispatches(path) if cutoff <= item.timestamp <= now
400
462
  ]
401
- empty: LedgerData = {"rows": [], "unowned_codex": 0, "unowned_agy": 0}
463
+ empty: LedgerData = {
464
+ "rows": [],
465
+ "unowned_codex": 0,
466
+ "unowned_agy": 0,
467
+ "used_pct": used_pct or {},
468
+ }
402
469
  if not dispatches:
403
470
  return empty
404
471
  known_sessions = {path.stem for path in paths}
@@ -426,32 +493,51 @@ def build_ledger(estimates: dict[tuple[str, str], float | None], now: datetime)
426
493
  origin = by_owner.get(owner)
427
494
  if not origin:
428
495
  continue
429
- shares = {"claude-code": estimates.get(("claude-code", owner))}
430
- for agent_id in ("codex", "antigravity"):
431
- values = [estimates.get((agent_id, session_id)) for session_id in children[agent_id]]
432
- shares[agent_id] = (
433
- sum(value for value in values if value is not None)
434
- if any(value is not None for value in values)
435
- else None
436
- )
496
+ claude_pct = estimates.get(("claude-code", owner, "claude-code"))
497
+ codex_values = [
498
+ estimates[("codex", child, "codex")]
499
+ for child in children["codex"]
500
+ if ("codex", child, "codex") in estimates
501
+ ]
502
+ codex_pct = sum(codex_values) if codex_values else None
503
+ agy_groups: dict[str, float] = {}
504
+ for (agent, child, group), value in estimates.items():
505
+ if agent == "antigravity" and child in children[agent]:
506
+ name = group.removeprefix("antigravity:")
507
+ agy_groups[name] = agy_groups.get(name, 0.0) + value
508
+ reset_agents = {
509
+ agent
510
+ for agent, session, _ in reset_sessions or set()
511
+ if (agent == "claude-code" and session == owner)
512
+ or session in children.get(agent, set())
513
+ }
437
514
  first = min(origin, key=lambda item: item.timestamp)
438
515
  rows.append(
439
516
  (
440
517
  owner,
441
518
  {
442
519
  "date": first.timestamp.astimezone().strftime("%Y-%m-%d"),
443
- "project": Path(first.cwd).name or "unknown",
520
+ "project": resolve_project_name(first.cwd),
444
521
  "title": "",
445
522
  "dispatch_count": len(origin),
446
- "claude_pct": shares["claude-code"],
447
- "codex_pct": shares["codex"],
448
- "agy_pct": shares["antigravity"],
449
- "total_pct": sum(value for value in shares.values() if value is not None),
523
+ "claude_pct": claude_pct,
524
+ "codex_pct": codex_pct,
525
+ "agy_groups": agy_groups,
526
+ "claude_reset": claude_pct is None and "claude-code" in reset_agents,
527
+ "codex_reset": codex_pct is None and "codex" in reset_agents,
528
+ "agy_reset": not agy_groups and "antigravity" in reset_agents,
529
+ "total_pct": (claude_pct or 0) + (codex_pct or 0) + sum(agy_groups.values()),
450
530
  },
451
531
  )
452
532
  )
453
533
  rows.sort(key=lambda item: item[1]["total_pct"], reverse=True)
454
- for owner, row in rows[:15]:
455
- row["title"] = _session_title(path_by_session[owner])
456
- empty["rows"].append(row)
534
+ empty["rows"] = [row for _, row in rows]
535
+ displayed = {
536
+ id(row)
537
+ for block in ledger_blocks(empty["rows"], empty["used_pct"])
538
+ for row, _ in block["conversations"]
539
+ }
540
+ for owner, row in rows:
541
+ if id(row) in displayed:
542
+ row["title"] = _session_title(path_by_session[owner])
457
543
  return empty
@@ -1258,7 +1258,13 @@ def build_report_data(agents: list[AgentInfo], period: str = "month") -> ReportD
1258
1258
  entry for entry in weekly_entries if not ledger_backfill.is_ledger_entry(entry)
1259
1259
  ]
1260
1260
  windows = dispatch_ledger.load_windows() if weekly_entries else {}
1261
- estimates = dispatch_ledger.estimate_sessions(weekly_entries, windows, now)
1261
+ group_estimates, reset_sessions = dispatch_ledger.estimate_sessions(
1262
+ weekly_entries, windows, now
1263
+ )
1264
+ estimates: dict[tuple[str, str], float] = {}
1265
+ for (agent_id, session_id, _), value in group_estimates.items():
1266
+ key = (agent_id, session_id)
1267
+ estimates[key] = estimates.get(key, 0.0) + value
1262
1268
  sessions_by_cost = sorted(
1263
1269
  aggregate_sessions(real_entries), key=lambda session: session.cost_usd, reverse=True
1264
1270
  )
@@ -1322,7 +1328,12 @@ def build_report_data(agents: list[AgentInfo], period: str = "month") -> ReportD
1322
1328
  "sessions": session_rows,
1323
1329
  "daily_trend": daily_trend,
1324
1330
  "top_sessions": top_sessions,
1325
- "dispatch_ledger": dispatch_ledger.build_ledger(estimates, now),
1331
+ "dispatch_ledger": dispatch_ledger.build_ledger(
1332
+ group_estimates,
1333
+ now,
1334
+ reset_sessions,
1335
+ {key: window.used_pct for key, window in windows.items()},
1336
+ ),
1326
1337
  "comparison": comparison,
1327
1338
  "subscriptions": subscription.load_subscriptions(),
1328
1339
  "persona": _load_persona_for_period(period),
@@ -223,16 +223,7 @@
223
223
  "report_cost": "API 等值",
224
224
  "report_quota_share": "約佔週額度",
225
225
  "report_quota_approx": "約 {value}%",
226
- "report_ledger_section": "派工帳本(最近 7 天)",
227
- "report_ledger_date": "日期",
228
- "report_ledger_title": "對話標題",
229
- "report_ledger_count": "派工次數",
230
- "report_ledger_claude": "Claude 週額度",
231
- "report_ledger_codex": "Codex 週額度",
232
- "report_ledger_agy": "Antigravity 週額度",
233
- "report_ledger_unowned_part": "{count} 個 {agent}",
234
- "report_ledger_unowned_sep": "、",
235
- "report_ledger_unowned": "另有 {items} 對話找不到由 Claude 派工的紀錄,未列入上表",
226
+ "report_ledger_section": "派工額度花在哪(最近 7 天)",
236
227
  "report_daily_chart_title": "每日用量",
237
228
  "report_daily_chart_mode_tokens": "Tokens",
238
229
  "report_daily_chart_mode_cost": "API 等值",
@@ -320,9 +311,9 @@
320
311
  "report_wrapped_top_project": "最常用專案",
321
312
  "report_wrapped_books_equivalent": "≈ {books} 本書的字量",
322
313
  "report_wrapped_beast_phoenix_title": "鳳凰加冕",
323
- "report_wrapped_beast_phoenix_caption": "今年 Claude 的火力略勝一籌,像把灰燼一口氣吹回天空。",
314
+ "report_wrapped_beast_phoenix_caption": "近一年 Claude 的火力略勝一籌,像把灰燼一口氣吹回天空。",
324
315
  "report_wrapped_beast_dragon_title": "巨龍加冕",
325
- "report_wrapped_beast_dragon_caption": "今年 Codex 的火力更猛,像一條龍一路把工地燒到完工。",
316
+ "report_wrapped_beast_dragon_caption": "近一年 Codex 的火力更猛,像一條龍一路把工地燒到完工。",
326
317
  "project_butler": "接著上次做",
327
318
  "project_butler_tooltip": "開新對話時,自動把上次的進度交給 AI,不用再重講一次。",
328
319
  "terse_mode_menu": "省 token 模式",
@@ -552,14 +543,19 @@
552
543
  "claude_desktop_updated": "Claude 桌面版 · {minutes} 分鐘前更新",
553
544
  "claude_desktop_stale": "Claude 桌面版 · 資料過期 · {minutes} 分鐘前更新",
554
545
  "report_project_others": "其他專案",
555
- "report_ledger_pct_note": "Claude、Codex、Antigravity 三欄是約佔該工具週額度的百分比。",
546
+ "report_ledger_lead": "Claude 派給 Codex、Antigravity 的工作,各吃掉多少週額度。",
556
547
  "report_trend_partial_week": "僅 {days} 天",
557
548
  "report_history_scope": "分工具紀錄從 {date} 起;更早的每日總量只在下方熱力圖",
558
549
  "report_composition_missing": "另有 {tokens} tokens 沒有輸入/輸出拆解",
559
550
  "report_wrapped_cost_scope": "只含有價格紀錄的日子({date} 起)",
560
551
  "report_wrapped_cost_no_records": "沒有價格紀錄",
561
- "report_wrapped_beast_phoenix_caption_dominant": "今年 Claude 的用量遙遙領先。",
562
- "report_wrapped_beast_dragon_caption_dominant": "今年 Codex 的用量遙遙領先。"
552
+ "report_wrapped_beast_phoenix_caption_dominant": "近一年 Claude 的用量遙遙領先。",
553
+ "report_wrapped_beast_dragon_caption_dominant": "近一年 Codex 的用量遙遙領先。",
554
+ "report_ledger_used": "本週已用 {pct}%",
555
+ "report_ledger_row_meta": "{project} · 派工 {count} 次",
556
+ "report_ledger_more": "另外 {count} 段派工對話",
557
+ "report_ledger_other": "其他(你直接用的,或沒對到派工紀錄)",
558
+ "report_ledger_all_reset": "最近 7 天的派工都在額度重置前,算在上週。"
563
559
  },
564
560
  "en": {
565
561
  "panel_default_name": "Default",
@@ -785,16 +781,7 @@
785
781
  "report_cost": "API equivalent",
786
782
  "report_quota_share": "Approx. weekly quota",
787
783
  "report_quota_approx": "about {value}%",
788
- "report_ledger_section": "Dispatch ledger (last 7 days)",
789
- "report_ledger_date": "Date",
790
- "report_ledger_title": "Conversation title",
791
- "report_ledger_count": "Dispatches",
792
- "report_ledger_claude": "Claude weekly quota",
793
- "report_ledger_codex": "Codex weekly quota",
794
- "report_ledger_agy": "Antigravity weekly quota",
795
- "report_ledger_unowned_part": "{count} {agent}",
796
- "report_ledger_unowned_sep": ", ",
797
- "report_ledger_unowned": "No record of dispatch by Claude was found for another {items} conversations; they are not included in the table above",
784
+ "report_ledger_section": "Where your dispatch quota went (last 7 days)",
798
785
  "report_daily_chart_title": "Daily usage",
799
786
  "report_daily_chart_mode_tokens": "Tokens",
800
787
  "report_daily_chart_mode_cost": "API equivalent",
@@ -882,9 +869,9 @@
882
869
  "report_wrapped_top_project": "Top project",
883
870
  "report_wrapped_books_equivalent": "≈ {books} books worth of words",
884
871
  "report_wrapped_beast_phoenix_title": "Phoenix Crowned",
885
- "report_wrapped_beast_phoenix_caption": "Claude carried slightly more fire this year, like a bird kicking the whole workshop back into the sky.",
872
+ "report_wrapped_beast_phoenix_caption": "Claude carried slightly more fire over the past year, like a bird kicking the whole workshop back into the sky.",
886
873
  "report_wrapped_beast_dragon_title": "Dragon Crowned",
887
- "report_wrapped_beast_dragon_caption": "Codex hit harder this year, like a dragon torching a build all the way to done.",
874
+ "report_wrapped_beast_dragon_caption": "Codex hit harder over the past year, like a dragon torching a build all the way to done.",
888
875
  "project_butler": "Resume Last Session",
889
876
  "project_butler_tooltip": "When you open a new session, automatically hand your last progress to the AI — no need to re-explain.",
890
877
  "terse_mode_menu": "Token Saver",
@@ -1114,14 +1101,19 @@
1114
1101
  "claude_desktop_updated": "Claude Desktop · updated {minutes}m ago",
1115
1102
  "claude_desktop_stale": "Claude Desktop · stale · updated {minutes}m ago",
1116
1103
  "report_project_others": "Other projects",
1117
- "report_ledger_pct_note": "The Claude, Codex, and Antigravity columns are approximate shares of each tool’s weekly quota.",
1104
+ "report_ledger_lead": "How much of each weekly quota went to work Claude handed to Codex and Antigravity.",
1118
1105
  "report_trend_partial_week": "only {days} days",
1119
1106
  "report_history_scope": "Per-tool records start on {date}; earlier daily totals appear only in the heatmap below.",
1120
1107
  "report_composition_missing": "Another {tokens} tokens have no input/output breakdown.",
1121
1108
  "report_wrapped_cost_scope": "Only days with price records (from {date})",
1122
1109
  "report_wrapped_cost_no_records": "No price records",
1123
- "report_wrapped_beast_phoenix_caption_dominant": "Claude led by a wide margin in tokens this year.",
1124
- "report_wrapped_beast_dragon_caption_dominant": "Codex led by a wide margin in tokens this year."
1110
+ "report_wrapped_beast_phoenix_caption_dominant": "Claude led by a wide margin in tokens over the past year.",
1111
+ "report_wrapped_beast_dragon_caption_dominant": "Codex led by a wide margin in tokens over the past year.",
1112
+ "report_ledger_used": "{pct}% used this week",
1113
+ "report_ledger_row_meta": "{project} · {count} dispatches",
1114
+ "report_ledger_more": "{count} more dispatching conversations",
1115
+ "report_ledger_other": "Other (your own use, or no dispatch record found)",
1116
+ "report_ledger_all_reset": "All dispatched work in the last 7 days came before the quota reset, so it counts toward last week."
1125
1117
  },
1126
1118
  "zh-CN": {
1127
1119
  "panel_default_name": "默认",
@@ -1347,16 +1339,7 @@
1347
1339
  "report_cost": "API 等值",
1348
1340
  "report_quota_share": "约占周额度",
1349
1341
  "report_quota_approx": "约 {value}%",
1350
- "report_ledger_section": "派工账本(最近 7 天)",
1351
- "report_ledger_date": "日期",
1352
- "report_ledger_title": "对话标题",
1353
- "report_ledger_count": "派工次数",
1354
- "report_ledger_claude": "Claude 周额度",
1355
- "report_ledger_codex": "Codex 周额度",
1356
- "report_ledger_agy": "Antigravity 周额度",
1357
- "report_ledger_unowned_part": "{count} 个 {agent}",
1358
- "report_ledger_unowned_sep": "、",
1359
- "report_ledger_unowned": "另有 {items} 对话找不到由 Claude 派工的记录,未列入上表",
1342
+ "report_ledger_section": "派工额度花在哪(最近 7 天)",
1360
1343
  "report_daily_chart_title": "每日用量",
1361
1344
  "report_daily_chart_mode_tokens": "Tokens",
1362
1345
  "report_daily_chart_mode_cost": "API 等值",
@@ -1444,9 +1427,9 @@
1444
1427
  "report_wrapped_top_project": "最常用项目",
1445
1428
  "report_wrapped_books_equivalent": "≈ {books} 本书的字量",
1446
1429
  "report_wrapped_beast_phoenix_title": "凤凰加冕",
1447
- "report_wrapped_beast_phoenix_caption": "今年 Claude 的火力略胜一筹,像把灰烬一口气吹回天空。",
1430
+ "report_wrapped_beast_phoenix_caption": "近一年 Claude 的火力略胜一筹,像把灰烬一口气吹回天空。",
1448
1431
  "report_wrapped_beast_dragon_title": "巨龙加冕",
1449
- "report_wrapped_beast_dragon_caption": "今年 Codex 的火力更猛,像一条龙一路把工地烧到完工。",
1432
+ "report_wrapped_beast_dragon_caption": "近一年 Codex 的火力更猛,像一条龙一路把工地烧到完工。",
1450
1433
  "project_butler": "接着上次做",
1451
1434
  "project_butler_tooltip": "开新对话时,自动把上次的进度交给 AI,不用再重讲一次。",
1452
1435
  "terse_mode_menu": "省 token 模式",
@@ -1676,14 +1659,19 @@
1676
1659
  "claude_desktop_updated": "Claude 桌面版 · {minutes} 分钟前更新",
1677
1660
  "claude_desktop_stale": "Claude 桌面版 · 数据过期 · {minutes} 分钟前更新",
1678
1661
  "report_project_others": "其他项目",
1679
- "report_ledger_pct_note": "Claude、Codex、Antigravity 三列是约占该工具周额度的百分比。",
1662
+ "report_ledger_lead": "Claude 派给 Codex、Antigravity 的工作,各吃掉多少周额度。",
1680
1663
  "report_trend_partial_week": "仅 {days} 天",
1681
1664
  "report_history_scope": "分工具记录从 {date} 起;更早的每日总量只在下方热力图",
1682
1665
  "report_composition_missing": "另有 {tokens} tokens 没有输入/输出拆解",
1683
1666
  "report_wrapped_cost_scope": "只含有价格记录的日子({date} 起)",
1684
1667
  "report_wrapped_cost_no_records": "没有价格记录",
1685
- "report_wrapped_beast_phoenix_caption_dominant": "今年 Claude 的用量遥遥领先。",
1686
- "report_wrapped_beast_dragon_caption_dominant": "今年 Codex 的用量遥遥领先。"
1668
+ "report_wrapped_beast_phoenix_caption_dominant": "近一年 Claude 的用量遥遥领先。",
1669
+ "report_wrapped_beast_dragon_caption_dominant": "近一年 Codex 的用量遥遥领先。",
1670
+ "report_ledger_used": "本周已用 {pct}%",
1671
+ "report_ledger_row_meta": "{project} · 派工 {count} 次",
1672
+ "report_ledger_more": "另外 {count} 段派工对话",
1673
+ "report_ledger_other": "其他(你直接用的,或没对到派工记录)",
1674
+ "report_ledger_all_reset": "最近 7 天的派工都在额度重置前,算在上周。"
1687
1675
  },
1688
1676
  "ja": {
1689
1677
  "panel_default_name": "デフォルト",
@@ -1909,16 +1897,7 @@
1909
1897
  "report_cost": "API 換算",
1910
1898
  "report_quota_share": "週間枠の推定割合",
1911
1899
  "report_quota_approx": "約 {value}%",
1912
- "report_ledger_section": "委任台帳(直近 7 日間)",
1913
- "report_ledger_date": "日付",
1914
- "report_ledger_title": "会話タイトル",
1915
- "report_ledger_count": "委任回数",
1916
- "report_ledger_claude": "Claude 週間クォータ",
1917
- "report_ledger_codex": "Codex 週間クォータ",
1918
- "report_ledger_agy": "Antigravity 週間クォータ",
1919
- "report_ledger_unowned_part": "{agent} {count} 件",
1920
- "report_ledger_unowned_sep": "、",
1921
- "report_ledger_unowned": "ほかに {items} 件の会話には Claude からのタスク割り当て記録が見つからず、上の表には含めていません",
1900
+ "report_ledger_section": "委任先で使ったクォータ(直近 7 日間)",
1922
1901
  "report_daily_chart_title": "日別の使用量",
1923
1902
  "report_daily_chart_mode_tokens": "Tokens",
1924
1903
  "report_daily_chart_mode_cost": "API 換算",
@@ -2006,9 +1985,9 @@
2006
1985
  "report_wrapped_top_project": "最も使ったプロジェクト",
2007
1986
  "report_wrapped_books_equivalent": "≈ {books} 冊分の文字量",
2008
1987
  "report_wrapped_beast_phoenix_title": "フェニックス戴冠",
2009
- "report_wrapped_beast_phoenix_caption": "今年は Claude の火力がわずかに上回り、灰を空へ吹き返すような一年でした。",
1988
+ "report_wrapped_beast_phoenix_caption": "この1年は Claude の火力がわずかに上回り、灰を空へ吹き返すような一年でした。",
2010
1989
  "report_wrapped_beast_dragon_title": "ドラゴン戴冠",
2011
- "report_wrapped_beast_dragon_caption": "今年は Codex の火力がより強く、工事現場を完成まで焼き切るような一年でした。",
1990
+ "report_wrapped_beast_dragon_caption": "この1年は Codex の火力がより強く、工事現場を完成まで焼き切るような一年でした。",
2012
1991
  "project_butler": "前回の続きから",
2013
1992
  "project_butler_tooltip": "新規セッションを開くと、前回の進捗を自動で AI に引き継ぎます。説明し直す必要はありません。",
2014
1993
  "terse_mode_menu": "トークン節約モード",
@@ -2238,14 +2217,19 @@
2238
2217
  "claude_desktop_updated": "Claude デスクトップ · {minutes}分前に更新",
2239
2218
  "claude_desktop_stale": "Claude デスクトップ · 古いデータ · {minutes}分前に更新",
2240
2219
  "report_project_others": "その他のプロジェクト",
2241
- "report_ledger_pct_note": "Claude、Codex、Antigravity の各列は、各ツールの週間上限に対する推定使用率です。",
2220
+ "report_ledger_lead": "Claude が Codex・Antigravity に任せた作業が、それぞれの週間クォータをどれだけ使ったか。",
2242
2221
  "report_trend_partial_week": "{days} 日分のみ",
2243
2222
  "report_history_scope": "ツール別記録は {date} から。それ以前の日別合計は下のヒートマップにのみ表示します。",
2244
2223
  "report_composition_missing": "ほかに {tokens} tokens は入力/出力の内訳がありません。",
2245
2224
  "report_wrapped_cost_scope": "価格記録のある日のみ({date} 以降)",
2246
2225
  "report_wrapped_cost_no_records": "価格記録なし",
2247
- "report_wrapped_beast_phoenix_caption_dominant": "今年のトークン数は Claude が大きくリードしました。",
2248
- "report_wrapped_beast_dragon_caption_dominant": "今年のトークン数は Codex が大きくリードしました。"
2226
+ "report_wrapped_beast_phoenix_caption_dominant": "この1年のトークン数は Claude が大きくリードしました。",
2227
+ "report_wrapped_beast_dragon_caption_dominant": "この1年のトークン数は Codex が大きくリードしました。",
2228
+ "report_ledger_used": "今週は {pct}% 使用済み",
2229
+ "report_ledger_row_meta": "{project} · 委任 {count} 回",
2230
+ "report_ledger_more": "ほか {count} 件の委任元の会話",
2231
+ "report_ledger_other": "その他(直接使った分、または委任記録が見つからない分)",
2232
+ "report_ledger_all_reset": "直近 7 日間に委任した作業はすべてクォータのリセット前のため、先週分に含まれます。"
2249
2233
  },
2250
2234
  "ko": {
2251
2235
  "panel_default_name": "기본",
@@ -2471,16 +2455,7 @@
2471
2455
  "report_cost": "API 환산",
2472
2456
  "report_quota_share": "주간 한도 예상 비율",
2473
2457
  "report_quota_approx": "약 {value}%",
2474
- "report_ledger_section": "작업 위임 내역 (최근 7일)",
2475
- "report_ledger_date": "날짜",
2476
- "report_ledger_title": "대화 제목",
2477
- "report_ledger_count": "위임 횟수",
2478
- "report_ledger_claude": "Claude 주간 한도",
2479
- "report_ledger_codex": "Codex 주간 한도",
2480
- "report_ledger_agy": "Antigravity 주간 한도",
2481
- "report_ledger_unowned_part": "{agent} {count}개",
2482
- "report_ledger_unowned_sep": ", ",
2483
- "report_ledger_unowned": "추가로 {items}개 대화는 Claude의 작업 배정 기록을 찾을 수 없어 위 표에서 제외했습니다",
2458
+ "report_ledger_section": "위임 작업에 쓴 한도 (최근 7일)",
2484
2459
  "report_daily_chart_title": "일별 사용량",
2485
2460
  "report_daily_chart_mode_tokens": "Tokens",
2486
2461
  "report_daily_chart_mode_cost": "API 환산",
@@ -2568,9 +2543,9 @@
2568
2543
  "report_wrapped_top_project": "가장 많이 쓴 프로젝트",
2569
2544
  "report_wrapped_books_equivalent": "≈ 책 {books}권 분량의 글자",
2570
2545
  "report_wrapped_beast_phoenix_title": "불사조 즉위",
2571
- "report_wrapped_beast_phoenix_caption": "올해는 Claude 쪽 화력이 조금 더 컸고, 재를 한 번에 하늘로 날려 보내는 듯했습니다.",
2546
+ "report_wrapped_beast_phoenix_caption": "최근 1년은 Claude 쪽 화력이 조금 더 컸고, 재를 한 번에 하늘로 날려 보내는 듯했습니다.",
2572
2547
  "report_wrapped_beast_dragon_title": "드래곤 즉위",
2573
- "report_wrapped_beast_dragon_caption": "올해는 Codex 쪽 화력이 더 강했고, 공사판을 완공까지 밀어붙이는 용 같았습니다.",
2548
+ "report_wrapped_beast_dragon_caption": "최근 1년은 Codex 쪽 화력이 더 강했고, 공사판을 완공까지 밀어붙이는 용 같았습니다.",
2574
2549
  "project_butler": "이어하기",
2575
2550
  "project_butler_tooltip": "새 세션을 열 때 지난 진행 상황을 AI에게 자동으로 전달합니다. 처음부터 다시 설명할 필요 없어요.",
2576
2551
  "terse_mode_menu": "토큰 절약 모드",
@@ -2800,13 +2775,18 @@
2800
2775
  "claude_desktop_updated": "Claude 데스크톱 · {minutes}분 전 업데이트",
2801
2776
  "claude_desktop_stale": "Claude 데스크톱 · 오래된 데이터 · {minutes}분 전 업데이트",
2802
2777
  "report_project_others": "기타 프로젝트",
2803
- "report_ledger_pct_note": "Claude, Codex, Antigravity 열은 각 도구의 주간 한도 대비 대략적인 사용 비율입니다.",
2778
+ "report_ledger_lead": "Claude가 Codex와 Antigravity에 맡긴 작업이 각 주간 한도를 얼마나 썼는지 보여 줍니다.",
2804
2779
  "report_trend_partial_week": "{days}일만 포함",
2805
2780
  "report_history_scope": "도구별 기록은 {date}부터입니다. 이전 일별 합계는 아래 히트맵에만 표시됩니다.",
2806
2781
  "report_composition_missing": "추가 {tokens} tokens에는 입력/출력 내역이 없습니다.",
2807
2782
  "report_wrapped_cost_scope": "가격 기록이 있는 날만 포함 ({date}부터)",
2808
2783
  "report_wrapped_cost_no_records": "가격 기록 없음",
2809
- "report_wrapped_beast_phoenix_caption_dominant": "올해 토큰 사용량은 Claude가 크게 앞섰습니다.",
2810
- "report_wrapped_beast_dragon_caption_dominant": "올해 토큰 사용량은 Codex가 크게 앞섰습니다."
2784
+ "report_wrapped_beast_phoenix_caption_dominant": "최근 1년 토큰 사용량은 Claude가 크게 앞섰습니다.",
2785
+ "report_wrapped_beast_dragon_caption_dominant": "최근 1년 토큰 사용량은 Codex가 크게 앞섰습니다.",
2786
+ "report_ledger_used": "이번 주 {pct}% 사용",
2787
+ "report_ledger_row_meta": "{project} · 작업 위임 {count}회",
2788
+ "report_ledger_more": "작업을 위임한 대화 {count}개 더",
2789
+ "report_ledger_other": "기타 (직접 사용했거나 위임 기록을 찾지 못한 사용량)",
2790
+ "report_ledger_all_reset": "최근 7일 동안 위임한 작업은 모두 한도 초기화 전이므로 지난주 사용량에 포함됩니다."
2811
2791
  }
2812
2792
  }
@@ -29,6 +29,7 @@ CACHE_PATH = Path.home() / ".usage/muse_quota.json"
29
29
  PROBE_DIR = Path.home() / ".usage/muse-probe"
30
30
  MUSE_MODEL = "muse-spark-1.3"
31
31
  STALE_SECONDS = 30 * 60
32
+ WINDOW_SECONDS = 5 * 3600
32
33
  _EXHAUSTED = re.compile(
33
34
  r"Subscription quota exhausted\. Your usage window resets at (\d{4}-\d\d-\d\dT[\d:.]+Z)"
34
35
  )
@@ -114,10 +115,13 @@ def parse_muse_allowance(stdout: str, checked_at: float) -> MuseQuotaResult | No
114
115
  return None
115
116
 
116
117
 
117
- def parse_exhausted_window(stdout: str, cached: MuseQuotaResult | None) -> MuseQuotaResult | None:
118
- """Read the 429 Muse returns once the 5-hour window is used up; it carries no allowance.
118
+ def parse_exhausted_window(
119
+ stdout: str, cached: MuseQuotaResult | None, now: float
120
+ ) -> MuseQuotaResult | None:
121
+ """Read the 429 Muse returns once a window is used up; it carries no allowance.
119
122
 
120
- Only the window's reset time is known, so the weekly reading and its age stay from ``cached``.
123
+ The reply names only a reset time. Within 5 hours it is the 5-hour window, so the weekly
124
+ reading and its age stay from ``cached``. Further out it can only be the weekly allowance.
121
125
  """
122
126
  match = _EXHAUSTED.search(stdout)
123
127
  if cached is None or match is None:
@@ -126,11 +130,17 @@ def parse_exhausted_window(stdout: str, cached: MuseQuotaResult | None) -> MuseQ
126
130
  reset = datetime.fromisoformat(match[1]).timestamp()
127
131
  except ValueError:
128
132
  return None
129
- return MuseQuotaResult(
130
- checked_at=cached["checked_at"],
131
- window=MuseQuotaWindow(used_percent=100.0, resets_at=reset),
132
- weekly=cached["weekly"],
133
- )
133
+ used_up = MuseQuotaWindow(used_percent=100.0, resets_at=reset)
134
+ if reset - now <= WINDOW_SECONDS:
135
+ return MuseQuotaResult(
136
+ checked_at=cached["checked_at"], window=used_up, weekly=cached["weekly"]
137
+ )
138
+ window = cached["window"]
139
+ # Nothing runs without weekly allowance, so a lapsed window is empty. One resetting past
140
+ # 5 hours is a weekly 429 that v0.34.5 recorded as the 5-hour window.
141
+ if not now < window["resets_at"] <= now + WINDOW_SECONDS:
142
+ window = MuseQuotaWindow(used_percent=0.0, resets_at=now)
143
+ return MuseQuotaResult(checked_at=now, window=window, weekly=used_up)
134
144
 
135
145
 
136
146
  def read_cache() -> MuseQuotaResult | None:
@@ -241,7 +251,7 @@ def _probe_worker(executable: str) -> None:
241
251
  if process.returncode == 0:
242
252
  result = parse_muse_allowance(stdout, time.time())
243
253
  else:
244
- result = parse_exhausted_window(stdout, read_cache())
254
+ result = parse_exhausted_window(stdout, read_cache(), time.time())
245
255
  if result is not None:
246
256
  _write_cache(result)
247
257
  succeeded = True
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "usage-cli"
3
- version = "0.34.5"
3
+ version = "0.34.6"
4
4
  description = "usage — 在 macOS menu bar 與 Windows 系統匣顯示 Claude Code、Codex、Antigravity 用量的小工具(也提供終端機 TUI)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"