coding-agent-cost 0.5.0__tar.gz → 0.5.1__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 (39) hide show
  1. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/PKG-INFO +1 -1
  2. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/__init__.py +1 -1
  3. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/cli.py +9 -4
  4. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/PKG-INFO +1 -1
  5. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/pyproject.toml +1 -1
  6. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_cli.py +51 -0
  7. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_version_consistency.py +7 -6
  8. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/LICENSE +0 -0
  9. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/README.md +0 -0
  10. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/aggregate.py +0 -0
  11. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/billing_plan.py +0 -0
  12. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/config.py +0 -0
  13. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/facts.py +0 -0
  14. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/rates.json +0 -0
  15. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/rates.py +0 -0
  16. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/__init__.py +0 -0
  17. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/claude.py +0 -0
  18. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/codex.py +0 -0
  19. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/renderers.py +0 -0
  20. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/SOURCES.txt +0 -0
  21. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/dependency_links.txt +0 -0
  22. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/entry_points.txt +0 -0
  23. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/top_level.txt +0 -0
  24. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/setup.cfg +0 -0
  25. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_aggregate.py +0 -0
  26. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_astra_pricing.py +0 -0
  27. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_billing_plan.py +0 -0
  28. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_e0a_fable_5_1_and_sonnet_5_correction.py +0 -0
  29. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_e0a_review3_hardening.py +0 -0
  30. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_facts.py +0 -0
  31. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_haiku_5_5_rates.py +0 -0
  32. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_measure_v1_contract.py +0 -0
  33. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_opus_5_5_rates.py +0 -0
  34. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_rates.py +0 -0
  35. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_claude.py +0 -0
  36. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_codex.py +0 -0
  37. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_dedup.py +0 -0
  38. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_output_lower_bound.py +0 -0
  39. {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_sonnet_5_5_rates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-agent-cost
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs
5
5
  Author: shiki-yusuke
6
6
  License: MIT
@@ -6,4 +6,4 @@ against a versioned rate catalog. Everything happens locally; there are no
6
6
  network calls.
7
7
  """
8
8
 
9
- __version__ = "0.5.0"
9
+ __version__ = "0.5.1"
@@ -50,10 +50,15 @@ def _parse_window_bound(value: Optional[str], tz: ZoneInfo) -> Optional[datetime
50
50
  dt = datetime.fromisoformat(text)
51
51
  except ValueError as exc:
52
52
  raise SystemExit(f"[error] invalid date/time: {value!r} ({exc})")
53
- if dt.tzinfo is not None:
54
- return dt.astimezone(timezone.utc)
55
- # Date-only (or naive datetime) input is interpreted in --timezone.
56
- return dt.replace(tzinfo=tz).astimezone(timezone.utc)
53
+ # Normalizing to UTC can overflow datetime's year range near
54
+ # 0001-01-01 / 9999-12-31 (e.g. "0001-01-01T00:00:00+23:59").
55
+ try:
56
+ if dt.tzinfo is not None:
57
+ return dt.astimezone(timezone.utc)
58
+ # Date-only (or naive datetime) input is interpreted in --timezone.
59
+ return dt.replace(tzinfo=tz).astimezone(timezone.utc)
60
+ except OverflowError:
61
+ raise SystemExit(f"[error] invalid date/time: {value!r} (out of range)") from None
57
62
 
58
63
 
59
64
  def _now_utc() -> datetime:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-agent-cost
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs
5
5
  Author: shiki-yusuke
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "coding-agent-cost"
7
- version = "0.5.0"
7
+ version = "0.5.1"
8
8
  description = "Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -4,6 +4,8 @@ import json
4
4
  import os
5
5
  import re
6
6
  import sqlite3
7
+ import subprocess
8
+ import sys
7
9
  from datetime import datetime, timezone
8
10
  from pathlib import Path
9
11
 
@@ -259,6 +261,55 @@ def test_invalid_since_still_rejected_after_z_suffix_fix(tmp_path, monkeypatch):
259
261
  assert raised # unchanged pre-existing behavior for report/export
260
262
 
261
263
 
264
+ def _run_cli_process(tmp_path, *argv):
265
+ """Run the CLI in a real interpreter so the process exit code, stdout and
266
+ stderr (including any traceback) are observed exactly as a user sees them."""
267
+ claude_home = tmp_path / "claude_home"
268
+ codex_home = tmp_path / "codex_home"
269
+ claude_home.mkdir(exist_ok=True)
270
+ codex_home.mkdir(exist_ok=True)
271
+ env = dict(os.environ)
272
+ env["CLAUDE_HOME"] = str(claude_home)
273
+ env["CODEX_HOME"] = str(codex_home)
274
+ env.pop("AGENT_COST_CONFIG", None)
275
+ repo_root = Path(__file__).resolve().parent.parent
276
+ env["PYTHONPATH"] = os.pathsep.join(filter(None, [str(repo_root), env.get("PYTHONPATH")]))
277
+ return subprocess.run(
278
+ [sys.executable, "-c", "import sys; from agent_cost import cli; sys.exit(cli.main(sys.argv[1:]))", *argv],
279
+ cwd=str(repo_root),
280
+ env=env,
281
+ stdin=subprocess.DEVNULL,
282
+ capture_output=True,
283
+ text=True,
284
+ timeout=60,
285
+ )
286
+
287
+
288
+ @pytest.mark.parametrize(
289
+ "argv, bad_value",
290
+ [
291
+ (["--since", "0001-01-01T00:00:00+23:59"], "0001-01-01T00:00:00+23:59"),
292
+ # Date-only input in a zone whose year-1 offset is positive (Asia/Tokyo
293
+ # LMT +09:18:59). Pacific/Kiritimati does not overflow here: its year-1
294
+ # offset is LMT -10:29:20.
295
+ (["--since", "0001-01-01", "--timezone", "Asia/Tokyo"], "0001-01-01"),
296
+ (["--until", "9999-12-31T23:59:59-12:00"], "9999-12-31T23:59:59-12:00"),
297
+ ],
298
+ )
299
+ def test_report_window_bound_utc_overflow_is_invalid_date_error(tmp_path, argv, bad_value):
300
+ baseline = _run_cli_process(tmp_path, "report", "--since", "not-a-date")
301
+ assert baseline.returncode != 0
302
+ assert "invalid date/time" in baseline.stderr
303
+
304
+ result = _run_cli_process(tmp_path, "report", *argv)
305
+ assert result.returncode == baseline.returncode
306
+ assert result.stdout == ""
307
+ assert "invalid date/time" in result.stderr
308
+ assert repr(bad_value) in result.stderr
309
+ assert "Traceback" not in result.stderr
310
+ assert "OverflowError" not in result.stderr
311
+
312
+
262
313
  def test_report_cli_includes_codex_thread_created_before_window(tmp_path, monkeypatch, capsys):
263
314
  # Regression (real report/export path, not just the reader unit test):
264
315
  # a Codex thread created well before the window must still contribute
@@ -1,7 +1,8 @@
1
1
  """Spec: pyproject.toml's [project].version and agent_cost.__version__ must
2
- agree, and both must read "0.5.0" for this change (``report --billing-plan
3
- PATH`` adds an opt-in internal_billing block; output without the flag is
4
- unchanged -- an additive change, so it bumps the minor version). A broken
2
+ agree, and both must read "0.5.1" for this change (``--since`` /
3
+ ``--until`` values whose UTC normalization overflows are rejected with the
4
+ usual ``[error] invalid date/time`` message instead of a traceback -- a bug
5
+ fix, so it bumps the patch version). A broken
5
6
  implementation would bump one file but not the other, or forget the bump
6
7
  entirely.
7
8
 
@@ -29,6 +30,6 @@ def test_pyproject_version_matches_package_version():
29
30
  assert _pyproject_version() == agent_cost.__version__
30
31
 
31
32
 
32
- def test_version_is_0_5_0():
33
- assert agent_cost.__version__ == "0.5.0"
34
- assert _pyproject_version() == "0.5.0"
33
+ def test_version_is_0_5_1():
34
+ assert agent_cost.__version__ == "0.5.1"
35
+ assert _pyproject_version() == "0.5.1"