ccburn 0.1.7__tar.gz → 0.2.0__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.
- {ccburn-0.1.7/src/ccburn.egg-info → ccburn-0.2.0}/PKG-INFO +23 -5
- {ccburn-0.1.7 → ccburn-0.2.0}/README.md +22 -4
- {ccburn-0.1.7 → ccburn-0.2.0}/pyproject.toml +1 -1
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/data/credentials.py +66 -15
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/display/gauges.py +2 -8
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/utils/formatting.py +2 -1
- {ccburn-0.1.7 → ccburn-0.2.0/src/ccburn.egg-info}/PKG-INFO +23 -5
- {ccburn-0.1.7 → ccburn-0.2.0}/tests/test_cli.py +3 -1
- {ccburn-0.1.7 → ccburn-0.2.0}/LICENSE +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/setup.cfg +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/__init__.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/app.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/cli.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/data/__init__.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/data/history.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/data/models.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/data/usage_client.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/display/__init__.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/display/chart.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/display/layout.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/main.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/utils/__init__.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn/utils/calculator.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn.egg-info/SOURCES.txt +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn.egg-info/dependency_links.txt +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn.egg-info/entry_points.txt +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn.egg-info/requires.txt +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/src/ccburn.egg-info/top_level.txt +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/tests/test_calculator.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/tests/test_formatting.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/tests/test_history.py +0 -0
- {ccburn-0.1.7 → ccburn-0.2.0}/tests/test_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccburn
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Terminal-based Claude Code usage limit visualizer with real-time burn-up charts
|
|
5
5
|
Author: JuanjoFuchs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -41,11 +41,12 @@ Dynamic: license-file
|
|
|
41
41
|
|
|
42
42
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
|
|
43
43
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
|
|
44
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
44
45
|
[](https://pypi.org/project/ccburn/)
|
|
45
46
|
[](https://pypi.org/project/ccburn/)
|
|
46
47
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
47
48
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
48
|
-
[](LICENSE)
|
|
49
50
|
|
|
50
51
|
<p align="center">
|
|
51
52
|
<img src="docs/cash1.png" alt="Burning tokens" width="140">
|
|
@@ -70,13 +71,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
|
|
|
70
71
|
|
|
71
72
|
## Installation
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
Run `claude` and login first to refresh credentials.
|
|
75
|
+
|
|
76
|
+
### WinGet (*pending approval*)
|
|
74
77
|
|
|
75
78
|
```powershell
|
|
76
79
|
winget install JuanjoFuchs.ccburn
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
###
|
|
82
|
+
### npx
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx ccburn
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### npm
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm install -g ccburn
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### pip
|
|
80
95
|
|
|
81
96
|
```bash
|
|
82
97
|
pip install ccburn
|
|
@@ -92,7 +107,10 @@ pip install -e ".[dev]"
|
|
|
92
107
|
|
|
93
108
|
## Quick Start
|
|
94
109
|
|
|
95
|
-
1. **
|
|
110
|
+
1. **Run Claude Code first** to ensure credentials are fresh:
|
|
111
|
+
```bash
|
|
112
|
+
claude
|
|
113
|
+
```
|
|
96
114
|
2. **Run ccburn:**
|
|
97
115
|
```bash
|
|
98
116
|
ccburn # Session limit (default)
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
|
|
4
4
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
5
6
|
[](https://pypi.org/project/ccburn/)
|
|
6
7
|
[](https://pypi.org/project/ccburn/)
|
|
7
8
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
8
9
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
9
|
-
[](LICENSE)
|
|
10
11
|
|
|
11
12
|
<p align="center">
|
|
12
13
|
<img src="docs/cash1.png" alt="Burning tokens" width="140">
|
|
@@ -31,13 +32,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
|
|
|
31
32
|
|
|
32
33
|
## Installation
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Run `claude` and login first to refresh credentials.
|
|
36
|
+
|
|
37
|
+
### WinGet (*pending approval*)
|
|
35
38
|
|
|
36
39
|
```powershell
|
|
37
40
|
winget install JuanjoFuchs.ccburn
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
###
|
|
43
|
+
### npx
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx ccburn
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### npm
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g ccburn
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### pip
|
|
41
56
|
|
|
42
57
|
```bash
|
|
43
58
|
pip install ccburn
|
|
@@ -53,7 +68,10 @@ pip install -e ".[dev]"
|
|
|
53
68
|
|
|
54
69
|
## Quick Start
|
|
55
70
|
|
|
56
|
-
1. **
|
|
71
|
+
1. **Run Claude Code first** to ensure credentials are fresh:
|
|
72
|
+
```bash
|
|
73
|
+
claude
|
|
74
|
+
```
|
|
57
75
|
2. **Run ccburn:**
|
|
58
76
|
```bash
|
|
59
77
|
ccburn # Session limit (default)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ccburn"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Terminal-based Claude Code usage limit visualizer with real-time burn-up charts"
|
|
9
9
|
authors = [{name = "JuanjoFuchs"}]
|
|
10
10
|
readme = "README.md"
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import platform
|
|
6
|
+
import subprocess
|
|
5
7
|
from datetime import datetime, timezone
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
@@ -44,32 +46,81 @@ def get_credentials_path() -> Path:
|
|
|
44
46
|
return Path.home() / ".claude" / ".credentials.json"
|
|
45
47
|
|
|
46
48
|
|
|
47
|
-
def
|
|
48
|
-
"""Read
|
|
49
|
+
def _read_credentials_from_keychain() -> dict | None:
|
|
50
|
+
"""Read credentials from macOS Keychain.
|
|
49
51
|
|
|
50
52
|
Returns:
|
|
51
|
-
Parsed credentials dictionary
|
|
53
|
+
Parsed credentials dictionary or None if not found/failed.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
result = subprocess.run(
|
|
57
|
+
["security", "find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
58
|
+
capture_output=True,
|
|
59
|
+
text=True,
|
|
60
|
+
check=True,
|
|
61
|
+
)
|
|
62
|
+
return json.loads(result.stdout.strip())
|
|
63
|
+
except (subprocess.CalledProcessError, json.JSONDecodeError, FileNotFoundError):
|
|
64
|
+
return None
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
|
|
67
|
+
def _read_credentials_from_file() -> dict | None:
|
|
68
|
+
"""Read credentials from file.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Parsed credentials dictionary or None if not found/failed.
|
|
56
72
|
"""
|
|
57
73
|
creds_path = get_credentials_path()
|
|
58
74
|
|
|
59
75
|
if not creds_path.exists():
|
|
60
|
-
|
|
61
|
-
f"Credentials file not found at {creds_path}\n"
|
|
62
|
-
"Please ensure Claude Code is installed and you are logged in.\n"
|
|
63
|
-
"Run 'claude' to log in if needed."
|
|
64
|
-
)
|
|
76
|
+
return None
|
|
65
77
|
|
|
66
78
|
try:
|
|
67
79
|
with open(creds_path) as f:
|
|
68
80
|
return json.load(f)
|
|
69
|
-
except json.JSONDecodeError
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
except (json.JSONDecodeError, PermissionError):
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def read_credentials() -> dict:
|
|
86
|
+
"""Read credentials from the appropriate storage.
|
|
87
|
+
|
|
88
|
+
On macOS, tries Keychain first, then falls back to file.
|
|
89
|
+
On Linux/Windows, reads from file.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Parsed credentials dictionary
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
CredentialsNotFoundError: If credentials not found
|
|
96
|
+
InvalidCredentialsError: If credentials are malformed
|
|
97
|
+
"""
|
|
98
|
+
credentials = None
|
|
99
|
+
|
|
100
|
+
# On macOS, try Keychain first
|
|
101
|
+
if platform.system() == "Darwin":
|
|
102
|
+
credentials = _read_credentials_from_keychain()
|
|
103
|
+
|
|
104
|
+
# Fall back to file (or use file on non-macOS)
|
|
105
|
+
if credentials is None:
|
|
106
|
+
credentials = _read_credentials_from_file()
|
|
107
|
+
|
|
108
|
+
if credentials is None:
|
|
109
|
+
creds_path = get_credentials_path()
|
|
110
|
+
if platform.system() == "Darwin":
|
|
111
|
+
raise CredentialsNotFoundError(
|
|
112
|
+
"Credentials not found in macOS Keychain or file.\n"
|
|
113
|
+
"Please ensure Claude Code is installed and you are logged in.\n"
|
|
114
|
+
"Run 'claude' to log in if needed."
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
raise CredentialsNotFoundError(
|
|
118
|
+
f"Credentials file not found at {creds_path}\n"
|
|
119
|
+
"Please ensure Claude Code is installed and you are logged in.\n"
|
|
120
|
+
"Run 'claude' to log in if needed."
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return credentials
|
|
73
124
|
|
|
74
125
|
|
|
75
126
|
def check_token_expired(credentials: dict) -> bool:
|
|
@@ -120,15 +120,9 @@ def create_gauge_section(
|
|
|
120
120
|
complete_style=Style(color=usage_color), # Bright color for filled
|
|
121
121
|
)
|
|
122
122
|
|
|
123
|
-
# Usage label
|
|
124
|
-
usage_emoji = "📊"
|
|
125
|
-
if limit_data.utilization >= 0.9:
|
|
126
|
-
usage_emoji = "🚨"
|
|
127
|
-
elif limit_data.utilization >= 0.75:
|
|
128
|
-
usage_emoji = "⚠️"
|
|
129
|
-
|
|
123
|
+
# Usage label - keep emoji consistent (⚠️ has inconsistent width across terminals)
|
|
130
124
|
usage_label = Text()
|
|
131
|
-
usage_label.append(
|
|
125
|
+
usage_label.append("📊 ", style="")
|
|
132
126
|
usage_label.append("Usage", style=f"bold {usage_color}")
|
|
133
127
|
|
|
134
128
|
table.add_row(
|
|
@@ -82,7 +82,8 @@ def format_reset_time(resets_at: datetime, now: datetime | None = None) -> str:
|
|
|
82
82
|
# Convert to local time for display
|
|
83
83
|
local_time = resets_at.astimezone()
|
|
84
84
|
day_name = local_time.strftime("%a") # "Tue"
|
|
85
|
-
|
|
85
|
+
# Use %I and strip leading zero (%-I is Unix-only, %#I is Windows-only)
|
|
86
|
+
time_str = local_time.strftime("%I:%M %p").lstrip("0") # "4:00 PM"
|
|
86
87
|
return f"Resets {day_name} {time_str}"
|
|
87
88
|
|
|
88
89
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccburn
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Terminal-based Claude Code usage limit visualizer with real-time burn-up charts
|
|
5
5
|
Author: JuanjoFuchs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -41,11 +41,12 @@ Dynamic: license-file
|
|
|
41
41
|
|
|
42
42
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
|
|
43
43
|
[](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
|
|
44
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
44
45
|
[](https://pypi.org/project/ccburn/)
|
|
45
46
|
[](https://pypi.org/project/ccburn/)
|
|
46
47
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
47
48
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
48
|
-
[](LICENSE)
|
|
49
50
|
|
|
50
51
|
<p align="center">
|
|
51
52
|
<img src="docs/cash1.png" alt="Burning tokens" width="140">
|
|
@@ -70,13 +71,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
|
|
|
70
71
|
|
|
71
72
|
## Installation
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
Run `claude` and login first to refresh credentials.
|
|
75
|
+
|
|
76
|
+
### WinGet (*pending approval*)
|
|
74
77
|
|
|
75
78
|
```powershell
|
|
76
79
|
winget install JuanjoFuchs.ccburn
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
###
|
|
82
|
+
### npx
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx ccburn
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### npm
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm install -g ccburn
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### pip
|
|
80
95
|
|
|
81
96
|
```bash
|
|
82
97
|
pip install ccburn
|
|
@@ -92,7 +107,10 @@ pip install -e ".[dev]"
|
|
|
92
107
|
|
|
93
108
|
## Quick Start
|
|
94
109
|
|
|
95
|
-
1. **
|
|
110
|
+
1. **Run Claude Code first** to ensure credentials are fresh:
|
|
111
|
+
```bash
|
|
112
|
+
claude
|
|
113
|
+
```
|
|
96
114
|
2. **Run ccburn:**
|
|
97
115
|
```bash
|
|
98
116
|
ccburn # Session limit (default)
|
|
@@ -51,10 +51,12 @@ class TestCLI:
|
|
|
51
51
|
"""Tests for CLI commands."""
|
|
52
52
|
|
|
53
53
|
def test_version(self):
|
|
54
|
+
import re
|
|
54
55
|
result = runner.invoke(app, ["--version"])
|
|
55
56
|
assert result.exit_code == 0
|
|
56
57
|
assert "ccburn" in result.stdout
|
|
57
|
-
|
|
58
|
+
# Check for semantic version pattern (e.g., 0.1.7)
|
|
59
|
+
assert re.search(r"\d+\.\d+\.\d+", result.stdout)
|
|
58
60
|
|
|
59
61
|
def test_help(self):
|
|
60
62
|
result = runner.invoke(app, ["--help"])
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|