Imported from rkdgusrnrlrl/ccusage-monitor (
AGENTS.md). Install upstream withnpx skills add rkdgusrnrlrl/ccusage-monitor. Copyright stays with the author.
ccusage-monitor 작업 가이드
제품 범위와 UI
- 이 프로젝트는 Codex, Claude, Cursor, CommandCode 사용량을 작은 항상 위 Windows 창에서 보여주는 도구다.
- 화면은 설정에 따라
Codex | Claude | Cursor | CommandCode 계정…을 가로로 이어 붙인다. Codex 열은 시작할 때 사용량을 한 번 읽어 값이 나왔을 때만 만든다. Claude 열은claude.enabled가false가 아니면 표시한다. Cursor 열은cursor.enabled가false가 아니면 표시한다. CommandCode 열은config.json의commandcode_accounts개수만큼만 표시한다(0~2). config.json이 없거나commandcode_accounts가 없으면 CommandCode 열을 만들지 않는다. 계정이 하나면 한 열만 만든다.- 창 너비는 보이는 열 수에 비례한다. 열 하나당 210px에 크롬 30px를 더한다(4열일 때 기존 870px).
- 각 열은 같은 높이의 3행 자리를 쓴다. Codex, Claude, CommandCode는 제목과
5h사이에 약간의 여백을 두고,5h와7d사이 여백은 Cursor 한 행 높이의 절반이다. Cursor는cur,api,bot을 여백 없이 붙여 넣는다. - Cursor
cur/api는 월간autoPercentUsed/apiPercentUsed다.bot은 Grok Bot 주간usagePercent다. - Cursor 월간
cur/api게이지에는 이번 주 일요일까지 월간 예산 중 써도 되는 한도를 세로 눈금으로 표시하고, 상세는pace ±Np · reset …으로 그 한도와의 차이를 보여 준다.bot·Codex·CommandCode는 기존 사용량/리셋 표시를 유지한다. - 게이지 색상은 80% 미만 파랑, 80~94% 주황, 95% 이상 빨강을 유지한다. Cursor 월간 pace의 주황/빨강은 상세 텍스트에만 적용한다.
- 기본 Windows 제목 표시줄은 의도적으로 숨겨져 있다. 앱 내부의 드래그 가능한 제목 바와
X닫기 버튼을 유지한다. - 창은 시작할 때
-topmost를 세우는 것으로 끝내지 않는다. Windows는 다른 앱의 전체화면, UAC 보안 데스크톱, 잠금 화면, 세션 재접속, explorer.exe 재시작 때WS_EX_TOPMOST를 임의로 떨어뜨린다. 그래서refresh()마다_keep_on_top()으로 다시 세운다. 이미-topmost가 있다는 이유로 이 호출을 지우지 않는다. _keep_on_top()은SWP_NOACTIVATE를 반드시 포함한다. 이게 빠지면 매초 포커스를 훔쳐 다른 창의 입력이 끊긴다. 또winfo_id()의 안쪽 HWND가 아니라GetParent()로 얻은 Tk wrapper HWND에 적용해야 실제로 동작한다.
인증 정보와 설정
- API 키, Codex 토큰,
config.json, 로컬 인증 파일, 로그, 민감한 값이 보이는 스크린샷은 절대 커밋하지 않는다. - 기본 설정 파일은
config.json이다. 실행 파일과 같은 폴더를 먼저 보고, 없으면 상위 프로젝트 폴더를 확인한다. Git에는 값이 비어 있는config.example.json만 올린다. config.json의commandcode_accounts에는 0~2개의 계정을 넣을 수 있고, 각 계정은id와api_key를 가져야 한다. 창에 CommandCode 열을 만들려면 이 키가 필요하다.id는 인증에 사용하지 않는 화면용 식별자다. 화면에CommandCode(<id>)로 표시된다.- 창은 CommandCode를 환경 변수나
~/.commandcode/auth.json만으로 켜지 않는다. CLIccusage.py만 기존 환경 변수와 로컬 인증 파일을 쓴다. ccusage.py는 현재 로컬 CommandCode 인증 파일의userId를 화면용 fallback 값으로만 읽을 수 있다. 진단 출력에 실제 값을 노출하지 않는다.config.json의cursor.enabled가false이면 Cursor 열을 생략하고 창 너비도 줄인다. 키가 없으면 Cursor는 켠 상태로 둔다.claude.enabled도 같은 방식으로 동작한다.config.json의claude.auto_refresh가false이면 만료된 Claude 토큰을 직접 갱신하지 않는다. 키가 없으면 갱신하는 쪽이 기본이다.
Codex 사용량 연동
- Codex 사용량은 현재 로그인된 로컬 Codex CLI의 app-server로 읽는다. 인증 토큰을 복사·파싱·로그·저장하지 않는다.
- 창은 시작할 때 Codex를 한 번 읽어(
_probe_codex) 실패하면 Codex 열을 만들지 않고 창 너비도 줄인다. 이 판단은 시작 때 한 번만 하고, 실행 중에 열을 넣거나 빼지 않는다. - 시작 조회는
CODEX_PROBE_TIMEOUT을 쓴다. app-server가 멈춰도 창이 그만큼만 기다리게 한다. 이 값을 기본CODEX_TIMEOUT으로 되돌리지 않는다. - 시작 조회에 성공하면 그 값을 첫 화면에 그대로 쓴다. 같은 값을 곧바로 다시 요청하지 않는다.
CodexRateLimitClientapp-server 프로세스는 갱신마다 새로 만들지 말고 계속 재사용한다. 실제 요청 또는 프로세스 실패 때만 재시작한다.- Windows에서는 실패 복구 또는 앱 종료 때만 Codex 프로세스 트리 전체를 종료한다. 갱신마다
codex를 실행하는 구조로 되돌리지 않는다. codex app-server와taskkill을 포함한 모든 보조 프로세스는 Windows 숨김 실행 옵션을 사용해야 한다. 재연결 중 콘솔 창이 나타나면 안 된다.- Codex 백엔드의 503·timeout은 일시적 제공자 오류일 수 있다. UI 오류와 구분하고, 상세 내용은
%LOCALAPPDATA%\ccusage-monitor\ccusage.log에만 남긴다.
Claude 사용량 연동
- Claude 사용량은 현재 로그인된 로컬 Claude Code 세션으로 읽는다.
~/.claude/.credentials.json(또는CLAUDE_CONFIG_DIR)의 OAuth 토큰을 요청 순간에만 읽고, 끝나면 메모리에서 버린다. 복사·로그·저장하지 않는다. GET https://api.anthropic.com/api/oauth/usage의five_hour·seven_dayutilization을 5h·7d 행에 쓴다. Claude Code의/usage가 쓰는 것과 같은 인터페이스다.- Claude 사용량 API는 1초마다 호출하지 않는다. 최소 30초 간격을 유지하고 직전 성공 값을 재사용한다.
- 캐시한 값을 계속 보여줄 때는 조용히 최신 값인 척하지 않는다.
STALE_AFTER_SECONDS가 지나면 상세 줄에 경과 시간을 붙이고 상태 줄에도 오류를 남긴다. - 액세스 토큰 수명은 8시간이라 하룻밤 자면 만료된다. 만료된 토큰으로는 요청을 보내지 않고, 저장된 리프레시 토큰으로 직접 갱신한 뒤 요청한다.
claude를 먼저 실행해야만 동작하는 상태로 되돌리지 않는다. - 토큰 갱신에는 아래 안전장치를 모두 유지한다. 하나라도 빼지 않는다.
config.json의claude.auto_refresh가false면 갱신하지 않고 기존처럼 다시 로그인하라는 오류를 낸다.- 액세스 토큰이 실제로 만료됐을 때만 갱신한다. 미리 갱신하지 않는다.
- 갱신은
.credentials.json.ccusage.lock락으로 한 번에 하나만 수행한다. 오래된 락은LOCK_STALE_SECONDS가 지나면 회수한다. - 락을 잡은 뒤 파일을 다시 읽는다. 그 사이 Claude Code가 새 토큰을 썼으면 갱신하지 않고 그 토큰을 쓴다.
- 쓰기 전에
.credentials.json.ccusage.bak으로 백업하고, 임시 파일에 쓴 뒤os.replace로 교체하고, 다시 읽어 검증한다. 실패하면 원본으로 되돌린다. - 응답의
refresh_token은 rotation이므로 반드시 저장한다. 그 외 필드는 그대로 보존한다. - 갱신이 거절되면(
ClaudeRefreshRejected) 자격증명 파일이 바뀔 때까지 다시 시도하지 않는다. 일시적 실패는REFRESH_RETRY_SECONDS만큼 쉬었다 시도한다.
- 이 엔드포인트는 429를 쉽게 돌려준다. 실패했을 때도 다음 시도 시각을 반드시 뒤로 미루고, 429가 이어지면
RATE_LIMIT_BACKOFF_SECONDS사다리(60s→2m→5m→10m)로 물러난다. 응답에Retry-After가 있으면 사다리 값보다 긴 쪽을 따른다. - Claude 5h·7d는 퍼센트만 있는 창이라 상세 줄에
reset …만 표시한다.used / cap을 되살리지 않는다.
Cursor 사용량 연동
- Cursor 사용량은 현재 로그인된 로컬 Cursor IDE 세션으로 읽는다. 세션 토큰을 복사·로그·저장하지 않는다.
- 요청 순간에만 로컬 상태 DB에서 세션을 읽고, 요청이 끝나면 메모리에서 버린다.
- Cursor 월간 대시보드 API는 1초마다 호출하지 않는다. 최소 30초 간격을 유지하고 직전 성공 값을 재사용한다.
- Grok Bot 주간 사용량은 같은 세션으로
GetSandUsageStatus를 1초마다 읽는다. 실패하거나 한도가 없으면bot만 비우고 월간cur/api는 유지한다. - Cursor의 503·timeout은 일시적 제공자 오류일 수 있다. UI 오류와 구분하고, 상세 내용은
%LOCALAPPDATA%\ccusage-monitor\ccusage.log에만 남긴다.
빌드와 검증
-
기능 구현이나 수정이 끝나면 문법 검사 뒤에 배포용 exe도 다시 빌드한다.
-
배포용 실행 파일 이름은 반드시
dist\ccusage-monitor.exe하나만 사용한다. -
빌드 명령:
python -m PyInstaller --noconfirm --clean --onefile --windowed --name ccusage-monitor .\ccusage_window.pyw -
빌드 전
ccusage-monitor.exe가 실행 중인지 확인한다. 실행 중이면 Windows 파일 잠금 때문에 결과물을 덮어쓸 수 없다. -
dist/,build/은 Git에서 제외한다. 실행 파일과 일회성 빌드 spec은 커밋하지 않는다. -
Python 변경 뒤에는 최소한 아래 문법 검사를 실행한다.
python -m py_compile .\ccusage.py .\ccusage_window.pyw .\cursor_usage.py .\claude_usage.py -
계정 파싱 검증에는 placeholder나 mock만 사용한다. 실제 API 키나 인증 파일 내용을 출력하지 않는다.
Git 작업 방식
main이 아닌 작업 브랜치에서 변경한다.- 현재 작업과 관련된 소스, 문서, 안전한 예시 파일만 커밋한다.
- 브랜치를 push하고 PR을 생성하거나 갱신한다. 사용자의 명시적 승인 없이 PR을 merge하지 않는다.