cctally 1.96.2 → 1.98.0
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 +41 -0
- package/bin/_cctally_dashboard.py +595 -46
- package/bin/_cctally_dashboard_envelope.py +57 -23
- package/bin/_cctally_dashboard_share.py +25 -5
- package/bin/_cctally_dashboard_sources.py +123 -36
- package/bin/_cctally_db.py +10 -0
- package/bin/_cctally_doctor.py +15 -10
- package/bin/_cctally_journal_repair.py +7 -6
- package/bin/_cctally_parser.py +47 -22
- package/bin/_cctally_tui.py +515 -63
- package/bin/_lib_alert_axes.py +8 -3
- package/bin/_lib_dashboard_sources.py +837 -102
- package/bin/_lib_journal_router.py +14 -0
- package/bin/_lib_share.py +191 -36
- package/bin/_lib_snapshot_cache.py +12 -0
- package/bin/cctally +48 -1
- package/dashboard/static/assets/index-CC8TTZUC.css +1 -0
- package/dashboard/static/assets/index-CChXFhs_.js +97 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +1 -1
- package/dashboard/static/assets/index-BRFMIN18.js +0 -97
- package/dashboard/static/assets/index-Crj7bzyj.css +0 -1
package/bin/_cctally_parser.py
CHANGED
|
@@ -58,6 +58,24 @@ class CLIHelpFormatter(
|
|
|
58
58
|
super().__init__(prog, **kwargs) # type: ignore[arg-type]
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
_PHYSICAL_PROVIDER_SOURCES = ("claude", "codex")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _analytics_source_choices() -> tuple[str, ...]:
|
|
65
|
+
"""Return physical providers plus the provider-separated combined view."""
|
|
66
|
+
return (*_PHYSICAL_PROVIDER_SOURCES, "all")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _provider_display_names() -> str:
|
|
70
|
+
"""Render the canonical physical-provider tuple for user-facing help."""
|
|
71
|
+
labels = tuple(source.title() for source in _PHYSICAL_PROVIDER_SOURCES)
|
|
72
|
+
if len(labels) == 1:
|
|
73
|
+
return labels[0]
|
|
74
|
+
if len(labels) == 2:
|
|
75
|
+
return " and ".join(labels)
|
|
76
|
+
return f"{', '.join(labels[:-1])}, and {labels[-1]}"
|
|
77
|
+
|
|
78
|
+
|
|
61
79
|
def _argparse_has_arg(parser, option_string: str) -> bool:
|
|
62
80
|
"""Return True if ``parser`` already registered ``option_string``."""
|
|
63
81
|
for action in parser._actions:
|
|
@@ -288,11 +306,12 @@ def _add_source_args(
|
|
|
288
306
|
"""Attach the source selector or pin a nested provider alias."""
|
|
289
307
|
if fixed_source is None:
|
|
290
308
|
parser.add_argument(
|
|
291
|
-
"--source", choices=(
|
|
292
|
-
|
|
309
|
+
"--source", choices=_analytics_source_choices(),
|
|
310
|
+
default=_PHYSICAL_PROVIDER_SOURCES[0],
|
|
311
|
+
help="Analytics provider.",
|
|
293
312
|
)
|
|
294
313
|
else:
|
|
295
|
-
if fixed_source not in
|
|
314
|
+
if fixed_source not in _PHYSICAL_PROVIDER_SOURCES:
|
|
296
315
|
raise ValueError(f"unsupported fixed source {fixed_source!r}")
|
|
297
316
|
parser.set_defaults(source=fixed_source)
|
|
298
317
|
if speed:
|
|
@@ -2717,8 +2736,9 @@ def _build_setup_parser(subparsers, name, *, help_text, xref=None):
|
|
|
2717
2736
|
formatter_class=CLIHelpFormatter,
|
|
2718
2737
|
description=textwrap.dedent(
|
|
2719
2738
|
"""\
|
|
2720
|
-
Install cctally
|
|
2721
|
-
~/.claude/settings.json
|
|
2739
|
+
Install cctally for local providers by adding Claude hook entries
|
|
2740
|
+
to ~/.claude/settings.json and native Codex handlers to every
|
|
2741
|
+
configured Codex home (all additive and idempotent). Also create
|
|
2722
2742
|
user-facing symlinks under ~/.local/bin/.
|
|
2723
2743
|
|
|
2724
2744
|
Modes (mutually exclusive):
|
|
@@ -2823,7 +2843,7 @@ def _build_transcript_parser(subparsers, name, *, help_text, xref=None):
|
|
|
2823
2843
|
formatter_class=CLIHelpFormatter)
|
|
2824
2844
|
t_search.add_argument("query", metavar="QUERY", help="Search text")
|
|
2825
2845
|
t_search.add_argument(
|
|
2826
|
-
"--source", choices=
|
|
2846
|
+
"--source", choices=_PHYSICAL_PROVIDER_SOURCES, default="claude",
|
|
2827
2847
|
help="Which provider's conversations to search (default: claude)")
|
|
2828
2848
|
t_search.add_argument(
|
|
2829
2849
|
"--account", metavar="REF", default=None,
|
|
@@ -3270,7 +3290,7 @@ def _build_hook_tick_parser(subparsers, name, *, help_text, xref=None):
|
|
|
3270
3290
|
help=argparse.SUPPRESS) # JSON string fed to mock fetch (tests only)
|
|
3271
3291
|
ht.add_argument("--foreground", action="store_true",
|
|
3272
3292
|
help=argparse.SUPPRESS) # Codex native hook wrapper
|
|
3273
|
-
ht.add_argument("--source", choices=
|
|
3293
|
+
ht.add_argument("--source", choices=_PHYSICAL_PROVIDER_SOURCES, default="claude",
|
|
3274
3294
|
help=argparse.SUPPRESS) # setup-managed native Codex hook
|
|
3275
3295
|
ht.set_defaults(func=c.cmd_hook_tick)
|
|
3276
3296
|
|
|
@@ -3572,7 +3592,7 @@ _REGISTRATION = (
|
|
|
3572
3592
|
_Reg('account', _build_account_parser, "Inspect per-provider account registry (list / show / label)", None, None),
|
|
3573
3593
|
_Reg('telemetry', _build_telemetry_parser, "Show or change anonymous install-count telemetry", None, None),
|
|
3574
3594
|
_Reg('alerts', _build_alerts_parser, "Manage threshold alerts", None, None),
|
|
3575
|
-
_Reg('setup', _build_setup_parser, "Install
|
|
3595
|
+
_Reg('setup', _build_setup_parser, "Install provider hooks/handlers + symlinks", None, None),
|
|
3576
3596
|
_Reg('db', _build_db_parser, "Migration / DB management (status, skip, unskip)", None, None),
|
|
3577
3597
|
_Reg('doctor', _build_doctor_parser, "Diagnose data freshness and install state", None, None),
|
|
3578
3598
|
_Reg('pricing-check', _build_pricing_check_parser, "Detect stale or missing embedded model pricing", None, None),
|
|
@@ -3592,28 +3612,33 @@ _REGISTRATION = (
|
|
|
3592
3612
|
|
|
3593
3613
|
def build_parser() -> argparse.ArgumentParser:
|
|
3594
3614
|
c = _cctally()
|
|
3615
|
+
providers = _provider_display_names()
|
|
3616
|
+
source_choices = ",".join(_analytics_source_choices())
|
|
3617
|
+
quick_start_reports = "\n".join(
|
|
3618
|
+
f" cctally report --source {source}"
|
|
3619
|
+
for source in _PHYSICAL_PROVIDER_SOURCES
|
|
3620
|
+
)
|
|
3595
3621
|
p = argparse.ArgumentParser(
|
|
3596
3622
|
prog="cctally",
|
|
3597
3623
|
formatter_class=CLIHelpFormatter,
|
|
3598
3624
|
description=textwrap.dedent(
|
|
3599
|
-
"""\
|
|
3600
|
-
Track
|
|
3601
|
-
in a local SQLite database.
|
|
3625
|
+
f"""\
|
|
3626
|
+
Track {providers} subscription usage and local USD cost in SQLite.
|
|
3602
3627
|
|
|
3603
3628
|
Data flow:
|
|
3604
|
-
1) Claude Code
|
|
3605
|
-
2)
|
|
3606
|
-
3)
|
|
3607
|
-
|
|
3629
|
+
1) Claude Code hooks and status lines retain Claude usage and sessions.
|
|
3630
|
+
2) Native Codex handlers retain Codex sessions and quota windows.
|
|
3631
|
+
3) Provider-aware project, diff, range-cost, cache-report, and report
|
|
3632
|
+
commands accept --source {{{source_choices}}}.
|
|
3633
|
+
4) Provider-native reports keep {providers} quota percentages and
|
|
3634
|
+
reset windows separate; `cctally codex quota` exposes Codex windows.
|
|
3608
3635
|
"""
|
|
3609
3636
|
),
|
|
3610
|
-
epilog=
|
|
3611
|
-
""
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
cctally report
|
|
3616
|
-
"""
|
|
3637
|
+
epilog=(
|
|
3638
|
+
"Quick start:\n"
|
|
3639
|
+
" cctally setup\n"
|
|
3640
|
+
f"{quick_start_reports}\n"
|
|
3641
|
+
" cctally codex quota statusline"
|
|
3617
3642
|
),
|
|
3618
3643
|
)
|
|
3619
3644
|
p.add_argument(
|