cctally 1.87.0 → 1.87.2
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.
- package/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/bin/_cctally_dashboard_envelope.py +33 -11
- package/bin/_cctally_tui.py +17 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.87.2] - 2026-07-30
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- The dashboard no longer freezes provider data while Claude or Codex sessions are continuously active. A completed read now publishes the coherent cache snapshot it actually built even when newer session bytes arrive before the build finishes, so Codex quota percentages and Recent Sessions keep advancing instead of remaining pinned to an older generation. Stats-side changes during a build still fail closed to the prior complete bundle.
|
|
12
|
+
|
|
13
|
+
## [1.87.1] - 2026-07-30
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Codex session names now remain visible in Recent Sessions when an individual account is selected, whenever transcript visibility is enabled. Selecting an account no longer replaces every session name with an em dash; disabling transcript visibility still hides names in both the all-accounts and focused-account views.
|
|
17
|
+
|
|
8
18
|
## [1.87.0] - 2026-07-30
|
|
9
19
|
|
|
10
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -30,9 +30,9 @@ Your Claude Code plan meters you with a percentage that creeps up all week. ccta
|
|
|
30
30
|
</p>
|
|
31
31
|
|
|
32
32
|
<!-- cctally:latest-stable:begin -->
|
|
33
|
-
**Latest stable: v1.
|
|
33
|
+
**Latest stable: v1.87.1** (2026-07-30)
|
|
34
34
|
|
|
35
|
-
-
|
|
35
|
+
- Codex session names now remain visible in Recent Sessions when an individual account is selected, whenever transcript visibility is enabled. Selecting an account no longer replaces every session name with an em dash; disabling transcript visibility still hides names in both the all-accounts and focused-account views.
|
|
36
36
|
<!-- cctally:latest-stable:end -->
|
|
37
37
|
|
|
38
38
|
## Quick start
|
|
@@ -1787,11 +1787,39 @@ def _overlay_claude_source_session_titles(
|
|
|
1787
1787
|
|
|
1788
1788
|
|
|
1789
1789
|
def _codex_source_session_rows(envelope: dict) -> list:
|
|
1790
|
-
"""Return
|
|
1790
|
+
"""Return every request-local Codex session row list.
|
|
1791
|
+
|
|
1792
|
+
Besides the merged parent used by ``All accounts``, decorated Codex data
|
|
1793
|
+
carries one ``account_scopes[*].sessions`` child per account. The client
|
|
1794
|
+
swaps that child into view when an account chip is focused, so the private
|
|
1795
|
+
label overlay must cover it under the same per-request transcript gate.
|
|
1796
|
+
"""
|
|
1791
1797
|
sources = envelope.get("sources")
|
|
1792
1798
|
if not isinstance(sources, Mapping):
|
|
1793
1799
|
return []
|
|
1794
1800
|
out = []
|
|
1801
|
+
|
|
1802
|
+
def append_rows(data) -> None:
|
|
1803
|
+
if not isinstance(data, Mapping):
|
|
1804
|
+
return
|
|
1805
|
+
sessions = data.get("sessions")
|
|
1806
|
+
if isinstance(sessions, Mapping):
|
|
1807
|
+
rows = sessions.get("rows")
|
|
1808
|
+
if isinstance(rows, list):
|
|
1809
|
+
out.append(rows)
|
|
1810
|
+
account_scopes = data.get("account_scopes")
|
|
1811
|
+
if not isinstance(account_scopes, Mapping):
|
|
1812
|
+
return
|
|
1813
|
+
for child in account_scopes.values():
|
|
1814
|
+
if not isinstance(child, Mapping):
|
|
1815
|
+
continue
|
|
1816
|
+
sessions = child.get("sessions")
|
|
1817
|
+
if not isinstance(sessions, Mapping):
|
|
1818
|
+
continue
|
|
1819
|
+
rows = sessions.get("rows")
|
|
1820
|
+
if isinstance(rows, list):
|
|
1821
|
+
out.append(rows)
|
|
1822
|
+
|
|
1795
1823
|
candidates = [(sources.get("codex") or {}).get("data")]
|
|
1796
1824
|
all_data = (sources.get("all") or {}).get("data")
|
|
1797
1825
|
if isinstance(all_data, Mapping):
|
|
@@ -1799,14 +1827,7 @@ def _codex_source_session_rows(envelope: dict) -> list:
|
|
|
1799
1827
|
if isinstance(providers, Mapping):
|
|
1800
1828
|
candidates.append(providers.get("codex"))
|
|
1801
1829
|
for data in candidates:
|
|
1802
|
-
|
|
1803
|
-
continue
|
|
1804
|
-
sessions = data.get("sessions")
|
|
1805
|
-
if not isinstance(sessions, Mapping):
|
|
1806
|
-
continue
|
|
1807
|
-
rows = sessions.get("rows")
|
|
1808
|
-
if isinstance(rows, list):
|
|
1809
|
-
out.append(rows)
|
|
1830
|
+
append_rows(data)
|
|
1810
1831
|
return out
|
|
1811
1832
|
|
|
1812
1833
|
|
|
@@ -1818,8 +1839,9 @@ def _overlay_codex_source_session_labels(
|
|
|
1818
1839
|
``state_5.sqlite.threads.title`` is derived from transcript prompt content.
|
|
1819
1840
|
The frozen source state therefore retains it only in a server-private key
|
|
1820
1841
|
map, outside the published ``data`` tree. Closed requests return before
|
|
1821
|
-
consulting that map; open requests match labels to
|
|
1822
|
-
their opaque resource keys. A missed call
|
|
1842
|
+
consulting that map; open requests match labels to merged-parent,
|
|
1843
|
+
account-scoped, and All rows by their opaque resource keys. A missed call
|
|
1844
|
+
fails closed.
|
|
1823
1845
|
"""
|
|
1824
1846
|
if not transcripts_visible:
|
|
1825
1847
|
return
|
package/bin/_cctally_tui.py
CHANGED
|
@@ -2808,21 +2808,33 @@ def _tui_build_source_bundle(
|
|
|
2808
2808
|
source_order=("claude", "codex", "all"),
|
|
2809
2809
|
sources={"claude": claude, "codex": codex, "all": combined},
|
|
2810
2810
|
)
|
|
2811
|
-
# End
|
|
2812
|
-
#
|
|
2813
|
-
#
|
|
2811
|
+
# End the pinned cache snapshot before re-reading the cheap signatures.
|
|
2812
|
+
# New cache commits after BEGIN do not make this bundle incoherent: all
|
|
2813
|
+
# cache-backed reads above saw the same frozen generation, and its
|
|
2814
|
+
# data_version names that generation. Reject only stats-side movement,
|
|
2815
|
+
# whose statement-scoped autocommit reads may have mixed generations.
|
|
2816
|
+
# Rejecting ordinary cache advancement starves publication whenever
|
|
2817
|
+
# active Claude/Codex sessions append faster than this build completes.
|
|
2814
2818
|
if cache_read_tx:
|
|
2815
2819
|
cache_conn.rollback()
|
|
2816
2820
|
cache_read_tx = False
|
|
2817
2821
|
post_stats_digest = codex_stats_digest(stats_conn)
|
|
2822
|
+
post_accounts_digest = accounts_identity_digest(stats_conn)
|
|
2818
2823
|
post_signature = c.compute_signature(
|
|
2819
2824
|
cache_conn,
|
|
2820
2825
|
stats_conn,
|
|
2821
2826
|
generation=c.current_generation(),
|
|
2822
2827
|
codex_stats_digest=post_stats_digest,
|
|
2823
|
-
accounts_digest=
|
|
2828
|
+
accounts_digest=post_accounts_digest,
|
|
2829
|
+
)
|
|
2830
|
+
stats_generation_moved = (
|
|
2831
|
+
post_signature.max_wus_id != signature.max_wus_id
|
|
2832
|
+
or post_signature.max_wcs_id != signature.max_wcs_id
|
|
2833
|
+
or post_signature.reset_sig != signature.reset_sig
|
|
2834
|
+
or post_stats_digest != stats_digest
|
|
2835
|
+
or post_accounts_digest != accounts_digest
|
|
2824
2836
|
)
|
|
2825
|
-
if
|
|
2837
|
+
if stats_generation_moved:
|
|
2826
2838
|
if prior_bundle is not None:
|
|
2827
2839
|
return prior_bundle
|
|
2828
2840
|
raise RuntimeError("source read generation moved during build")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cctally",
|
|
3
|
-
"version": "1.87.
|
|
3
|
+
"version": "1.87.2",
|
|
4
4
|
"description": "Claude Code usage tracker and local dashboard for Pro/Max subscription limits - weekly cost-per-percent trend, quota forecasts, threshold alerts. ccusage-compatible.",
|
|
5
5
|
"homepage": "https://github.com/omrikais/cctally",
|
|
6
6
|
"repository": {
|