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.
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/PKG-INFO +1 -1
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/__init__.py +1 -1
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/cli.py +9 -4
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/PKG-INFO +1 -1
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/pyproject.toml +1 -1
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_cli.py +51 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_version_consistency.py +7 -6
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/LICENSE +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/README.md +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/aggregate.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/billing_plan.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/config.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/facts.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/rates.json +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/rates.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/__init__.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/claude.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/readers/codex.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/agent_cost/renderers.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/SOURCES.txt +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/dependency_links.txt +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/entry_points.txt +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/top_level.txt +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/setup.cfg +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_aggregate.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_astra_pricing.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_billing_plan.py +0 -0
- {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
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_e0a_review3_hardening.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_facts.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_haiku_5_5_rates.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_measure_v1_contract.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_opus_5_5_rates.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_rates.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_claude.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_codex.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_dedup.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_reader_output_lower_bound.py +0 -0
- {coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/tests/test_sonnet_5_5_rates.py +0 -0
|
@@ -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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "coding-agent-cost"
|
|
7
|
-
version = "0.5.
|
|
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.
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
33
|
-
assert agent_cost.__version__ == "0.5.
|
|
34
|
-
assert _pyproject_version() == "0.5.
|
|
33
|
+
def test_version_is_0_5_1():
|
|
34
|
+
assert agent_cost.__version__ == "0.5.1"
|
|
35
|
+
assert _pyproject_version() == "0.5.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{coding_agent_cost-0.5.0 → coding_agent_cost-0.5.1}/coding_agent_cost.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|