usage-cli 0.29.32__py3-none-any.whl

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 (109) hide show
  1. adapters/__init__.py +5 -0
  2. adapters/agy.py +68 -0
  3. adapters/claude.py +215 -0
  4. adapters/codex.py +209 -0
  5. adapters/rate_limits.py +76 -0
  6. adapters/registry.py +17 -0
  7. adapters/types.py +139 -0
  8. agy_disk_cache.py +135 -0
  9. agy_loader.py +416 -0
  10. agy_quota_probe.py +748 -0
  11. agy_window_keeper.py +185 -0
  12. analyzer/__init__.py +5 -0
  13. analyzer/aggregator.py +139 -0
  14. analyzer/blocks.py +80 -0
  15. analyzer/diagnoser.py +638 -0
  16. analyzer/insights.py +277 -0
  17. analyzer/persona_loader.py +199 -0
  18. analyzer/reporter.py +989 -0
  19. analyzer/subscription.py +108 -0
  20. burn_rate.py +75 -0
  21. cache_quarantine.py +50 -0
  22. codex_disk_cache.py +227 -0
  23. codex_events.py +136 -0
  24. codex_fork_replay.py +111 -0
  25. codex_loader.py +1426 -0
  26. codex_paths.py +20 -0
  27. critter_frames.py +26 -0
  28. discussion_bridge.py +1196 -0
  29. discussion_cli.py +844 -0
  30. discussion_session.py +622 -0
  31. discussion_usage.py +13 -0
  32. discussion_window.py +955 -0
  33. disk_cache_common.py +132 -0
  34. disk_cache_lifecycle.py +39 -0
  35. doctor.py +452 -0
  36. fsevents_watch.py +207 -0
  37. history_disk_cache.py +110 -0
  38. history_loader.py +416 -0
  39. i18n.py +88 -0
  40. jsonl_limits.py +17 -0
  41. jsonl_utils.py +40 -0
  42. login_item.py +154 -0
  43. main.py +387 -0
  44. menubar.py +1201 -0
  45. menubar_actions.py +204 -0
  46. menubar_agy.py +193 -0
  47. menubar_chrome.py +156 -0
  48. menubar_menu.py +169 -0
  49. menubar_notify.py +102 -0
  50. menubar_popover.py +233 -0
  51. menubar_prefs.py +118 -0
  52. menubar_refresh.py +285 -0
  53. menubar_state.py +1200 -0
  54. menubar_title.py +157 -0
  55. menubar_update.py +123 -0
  56. panel_window.py +78 -0
  57. panel_window_state.py +159 -0
  58. panels/__init__.py +186 -0
  59. panels/base.py +83 -0
  60. panels/dynamic_height.py +140 -0
  61. panels/payload.py +178 -0
  62. panels/web_panel.py +513 -0
  63. panels/window_drag.py +56 -0
  64. prefs.py +44 -0
  65. pricing.py +452 -0
  66. project_resolver.py +112 -0
  67. service_status.py +383 -0
  68. session_hooks.py +1154 -0
  69. setup_app.py +171 -0
  70. setup_hook.py +1011 -0
  71. statusline_settings.py +160 -0
  72. talent_market_bridge.py +243 -0
  73. time_utils.py +24 -0
  74. tui.py +288 -0
  75. tui_sprite.py +206 -0
  76. ui/__init__.py +5 -0
  77. ui/html_report.py +923 -0
  78. ui/report_scripts.py +251 -0
  79. ui/report_styles.py +370 -0
  80. ui/tables.py +888 -0
  81. update_checker.py +156 -0
  82. update_gate.py +66 -0
  83. update_release_notes.py +49 -0
  84. usage_cli-0.29.32.data/data/share/usage/i18n.json +2427 -0
  85. usage_cli-0.29.32.dist-info/METADATA +223 -0
  86. usage_cli-0.29.32.dist-info/RECORD +109 -0
  87. usage_cli-0.29.32.dist-info/WHEEL +5 -0
  88. usage_cli-0.29.32.dist-info/entry_points.txt +3 -0
  89. usage_cli-0.29.32.dist-info/licenses/LICENSE +663 -0
  90. usage_cli-0.29.32.dist-info/top_level.txt +80 -0
  91. usage_cli.py +827 -0
  92. usage_client.py +487 -0
  93. usage_diagnosis_snapshot.py +143 -0
  94. usage_dir_sweeper.py +100 -0
  95. usage_lang.py +79 -0
  96. usage_logging.py +75 -0
  97. usage_notifications.py +96 -0
  98. usage_rate.py +97 -0
  99. usage_session_resume.py +913 -0
  100. usage_statusline.py +810 -0
  101. usage_statusline_agy.py +397 -0
  102. usage_statusline_forwarder.py +88 -0
  103. usage_terse_mode.py +223 -0
  104. usage_terse_reminder.py +151 -0
  105. win_login_item.py +53 -0
  106. window_keeper.py +264 -0
  107. windows_watch.py +443 -0
  108. wintray.py +2014 -0
  109. wintray_menu.py +136 -0
codex_fork_replay.py ADDED
@@ -0,0 +1,111 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (C) 2026 lollapalooza <https://github.com/aqua5230>
3
+ #
4
+ # Part of "usage". Free software licensed under the GNU Affero General Public
5
+ # License v3.0 only; see the LICENSE file for full terms and the warranty disclaimer.
6
+
7
+ """Stateless scanning for Codex fork-replay deduplication.
8
+
9
+ Fork logs rewrite replay timestamps but preserve the parent's cumulative token
10
+ sequence, so the boundary between replayed and new events is found by matching
11
+ token-usage prefixes against candidate parent logs. The LRU cache and its
12
+ orchestration (_fork_replay_boundary) stay in codex_loader; these helpers only
13
+ read the files they are handed.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from codex_events import (
22
+ _as_dict,
23
+ _as_str,
24
+ _load_json_line,
25
+ _token_usage_from_payload,
26
+ _TokenUsage,
27
+ )
28
+
29
+ _ReplayCacheKey = tuple[str, float, int, int] | None
30
+ _ReplayLookupKey = tuple[float, int, tuple[tuple[str, float, int], ...]]
31
+
32
+
33
+ def _fork_replay_lookup_key(
34
+ path: Path,
35
+ parent_paths: list[Path],
36
+ ) -> _ReplayLookupKey | None:
37
+ try:
38
+ child_stat = path.stat()
39
+ except OSError:
40
+ return None
41
+ parent_stats: list[tuple[str, float, int]] = []
42
+ for parent_path in parent_paths:
43
+ try:
44
+ parent_stat = parent_path.stat()
45
+ except OSError:
46
+ continue
47
+ parent_stats.append((str(parent_path), parent_stat.st_mtime, parent_stat.st_size))
48
+ return child_stat.st_mtime, child_stat.st_size, tuple(sorted(parent_stats))
49
+
50
+
51
+ def _token_usage_events_after_embedded_parent(
52
+ path: Path,
53
+ parent_session_id: str,
54
+ ) -> list[tuple[int, _TokenUsage]] | None:
55
+ embedded_parent = False
56
+ root_seen = False
57
+ events: list[tuple[int, _TokenUsage]] = []
58
+ try:
59
+ with path.open(encoding="utf-8") as file:
60
+ for line_number, line in enumerate(file, start=1):
61
+ data = _load_json_line(line)
62
+ if data is None:
63
+ continue
64
+ if data.get("type") == "session_meta":
65
+ session_id = _as_str(_as_dict(data.get("payload")).get("id"))
66
+ if not root_seen:
67
+ root_seen = True
68
+ elif session_id == parent_session_id:
69
+ embedded_parent = True
70
+ continue
71
+ usage = _token_usage_from_event(data)
72
+ if embedded_parent and usage is not None:
73
+ events.append((line_number, usage))
74
+ except (OSError, UnicodeDecodeError):
75
+ return None
76
+ return events if embedded_parent else None
77
+
78
+
79
+ def _raw_token_usage_sequence(path: Path) -> list[_TokenUsage]:
80
+ usage_events: list[_TokenUsage] = []
81
+ try:
82
+ with path.open(encoding="utf-8") as file:
83
+ for line in file:
84
+ data = _load_json_line(line)
85
+ if data is None:
86
+ continue
87
+ usage = _token_usage_from_event(data)
88
+ if usage is not None:
89
+ usage_events.append(usage)
90
+ except (OSError, UnicodeDecodeError):
91
+ return []
92
+ return usage_events
93
+
94
+
95
+ def _token_usage_from_event(data: dict[str, Any]) -> _TokenUsage | None:
96
+ if data.get("type") != "event_msg":
97
+ return None
98
+ payload = _as_dict(data.get("payload"))
99
+ if payload.get("type") != "token_count":
100
+ return None
101
+ usage = _as_dict(_as_dict(payload.get("info")).get("total_token_usage"))
102
+ return _token_usage_from_payload(usage) if usage else None
103
+
104
+
105
+ def _common_prefix_length(left: list[_TokenUsage], right: list[_TokenUsage]) -> int:
106
+ matched = 0
107
+ for left_usage, right_usage in zip(left, right, strict=False):
108
+ if left_usage != right_usage:
109
+ break
110
+ matched += 1
111
+ return matched