원문 캡처 · github.com
원문 캡처 · github.com

Codex Reset Tray는 macOS 메뉴 막대에서 Codex 사용량을 확인하고, 7일 사용량이 예정된 시각보다 일찍 복구된 것으로 보일 때 알림을 보내는 비공식 앱이다. 알림은 로컬 codex app-server가 반환하는 사용량 정보만으로 판단하며, codexreset.org의 예측은 메뉴에 표시할 뿐 알림에는 사용하지 않는다.

다운로드와 설치

Codex Reset Tray v0.1.0 범용판 다운로드(Apple Silicon·Intel)

DMG에는 CodexResetTray.app, 드래그해 설치할 수 있는 Applications 바로가기, 간단한 README, LICENSE, NOTICE가 들어 있다. 설치는 DMG를 열고 앱을 Applications 바로가기에 끌어다 놓은 뒤 응용 프로그램 폴더에서 실행하는 방식이다.

이 앱은 독립적으로 빌드해 임시 서명(ad-hoc signing)한 버전이며, 공증된 Developer ID 빌드는 아니다. 처음 실행할 때 macOS에서 앱을 마우스 오른쪽 버튼으로 눌러 ‘열기’를 선택하거나, 시스템 설정의 ‘개인정보 보호 및 보안’에서 ‘그래도 열기’를 눌러야 할 수 있다. 소스 코드는 공개돼 있으며 패키징 스크립트가 앱 번들의 서명과 내용을 확인한다. 자세한 내용은 빌드·테스트·패키징 안내에 있다.

다운로드한 파일은 다음 명령으로 확인할 수 있다.

shasum -a 256 -c CodexResetTray-v0.1.0-macos-universal.dmg.sha256```

## 메뉴 막대와 사용량 확인

메뉴 막대에는 항상 막대그래프 아이콘이 표시된다. 기본 설정에서는 현재 7일 사용량도 함께 표시하며, 예시는 `7d 89%`다. ‘Show Usage in Menu Bar’ 항목은 macOS 기본 메뉴 항목으로 표시되고 기본적으로 선택돼 있다. 이 항목을 끄면 아이콘만 남으며, 선택 상태는 `UserDefaults`의 `codexShowUsageInMenuBar` 키에 저장돼 다음 실행 때 복원된다. 오류가 발생해도 아이콘은 숨겨지지 않는다.

앱은 실행 직후와 이후 5분마다 로컬 `codex app-server --listen stdio://`를 시작한다. JSON-RPC `initialize` 핸드셰이크를 수행한 다음 `account/rateLimits/read`를 호출한다. 사용량만 읽으며 계정에 로그인하거나 로그아웃하지 않고 계정 상태도 변경하지 않는다. 각 호출에는 10초 제한 시간이 적용된다. `codex` 실행 파일이 없거나 실행에 실패한 경우, RPC 오류·시간 초과·잘못된 응답이 발생한 경우에는 오류를 메뉴에 표시하며 알림을 보내지 않는다.

7일 사용량 구간은 `windowDurationMins == 10080`인 항목으로 찾는다. 이 구간은 `primary` 또는 `secondary` 어느 쪽에 있을 수 있다. Codex가 일부 계정에서 주간 구간을 `primary`로 반환한 사례가 있기 때문이다. 앱은 해당 구간의 `resetsAt`을 이전 초기화 기한으로 사용한다.

## 조기 복구 알림 조건

앱은 직전 7일 사용량 기준값을 `UserDefaults`의 `codexWeeklyUsageBaselineV2`에 저장한다. 여기에는 직전 사용 비율, 직전 `resetsAt`, 기준값을 기록한 시각, 해당 초기화 기한에 대해 이미 알림을 보냈는지를 나타내는 표시가 포함된다. 알림은 다음 조건을 모두 충족할 때만 전송된다.

* 이전 기준값이 있어야 한다. 첫 번째 사용량 조회에서는 알림을 보내지 않는다.
* 직전 7일 사용량이 0%보다 커야 한다.
* 현재 사용량이 1% 이하여야 하며, 기준값보다 최소 5%포인트 낮아야 한다.
* 현재 시각이 이전 구간의 `resetsAt`보다 앞서야 한다.
* 같은 응답에서 `ordinaryUsageAllowed == true`여야 한다. 값이 `false`이거나 근거가 없으면 알림을 보내지 않는다.
* 해당 초기화 기한에 대해 알림을 이미 보낸 적이 없어야 한다.

초기화 구간마다 알림은 최대 한 번 전송된다. 예정된 초기화 시각이 도달했다는 이유만으로는 알림을 보내지 않는다. 이전 기한이 지난 뒤에도 보내지 않으며, 파싱이나 전송에 실패한 경우, codexreset.org의 예측이 바뀐 경우에도 알림을 보내지 않는다. 알림 권한은 첫 사용량 조회가 시작된 뒤에만 요청한다.

메뉴에는 `primary`·`secondary` 사용량 구간, 저장된 기준값과 기한, 최신 예측 또는 예측 오류, Codex 오류가 표시된다. `ordinaryUsageAllowed`, 마지막 조회 시각, 알림 상태는 각 항목의 툴팁에서 확인할 수 있다. ‘Refresh’, ‘Copy Status Summary’, ‘Show Usage in Menu Bar’, ‘Open codexreset.org’, ‘Quit’은 항상 메뉴에 있다. ‘Quit’은 항상 활성화되며 메뉴의 마지막 항목이다.

## 예측 정보

[Codex Reset Tray는 codexreset.org](https://codexreset.org/)의 예측을 메뉴가 열릴 때나 ‘Refresh’를 눌렀을 때만 가져온다. ‘Refresh’를 누르면 Codex 사용량도 즉시 다시 조회한다. 메뉴에는 24시간·48시간 복구 확률이 표시되며, 가장 최근에 보고된 복구 정보와 예측 요약은 메뉴와 해당 항목의 툴팁에서 볼 수 있다. 이 사이트의 정보는 참고용이며 알림 여부를 결정하는 데 사용하지 않는다.

## 요구 사항

* macOS 13 이상
* Swift가 포함된 Apple Command Line Tools. Xcode 프로젝트는 필요하지 않다.
* 설치돼 있고 ChatGPT 인증으로 로그인된 `codex` CLI
* v2 `account/rateLimits/read` 메서드를 지원하는 Codex app-server

앱은 일반적인 설치 위치와 `PATH`에서 `codex`를 찾는다. Finder에서 실행한 앱의 환경 변수가 제한적인 점을 고려해 Homebrew 경로도 확인한다. 자동 검색 대신 실행 파일 경로를 지정하려면 `CODEX_BINARY=/absolute/path/to/codex`를 설정한다.

## 빌드와 테스트

```sh
./build.sh
./test.sh
./scripts/package-release.sh

build.sh는 AppKit 앱을 컴파일하고 build/CodexResetTray.app에 임시 서명된 번들을 만든다. 범용 바이너리를 만들려면 BUILD_ARCHS="arm64 x86_64"를 설정한다. package-release.sh는 기본적으로 이 설정을 사용한다. build.sh는 앱을 설치하거나 실행하지 않는다.

test.sh는 픽스처를 이용한 Codex JSON·상태 전이 테스트, 메뉴 모델 테스트, 오프스크린 AppKit 통합 테스트, 저장소에 포함된 픽스처를 이용한 파서 스모크 테스트, 번들·아이콘·서명 검사를 실행한다. 번들 테스트가 빌드된 앱을 확인하므로 ./test.sh 전에 ./build.sh를 실행해야 한다.

./scripts/package-release.sh는 범용 앱을 빌드하고 앱, Applications 바로가기, README, LICENSE, NOTICE가 들어 있는 dist/CodexResetTray-v<version>-macos-universal.dmg를 만든다. 이어 .sha256 파일을 쓰고 DMG를 검증한다. 검증은 이미지가 정상인지 확인하고 읽기 전용으로 연결한 뒤 번들 식별자·버전·아이콘, 앱 코드 서명, arm64와 x86_64 포함 여부를 확인한다. 연결한 이미지에 쓰기를 시도할 수 없는지도 검사한 후 이미지를 분리한다.

Tests/TrayMenuRuntimeTests.swift는 주입한 메뉴 소스, 마운트되지 않은 상태 버튼 호스트, 가짜 URL 열기 처리기, 가짜 종료 처리기를 사용해 실제 NSMenu와 NSMenuItem 인스턴스를 테스트한다. 메뉴가 처음 열리기 전과 열린 뒤에도 비어 있지 않은지, 상태 버튼에 투명하지 않은 픽셀이 있는 템플릿 이미지가 있는지 확인한다. 상태·사용량 구간·기준값·예측·예측 세부 정보·오류 행도 검사한다. ‘Show Usage in Menu Bar’가 두 상태에서 올바르게 선택되는지와 사용량 텍스트를 숨겨도 아이콘이 표시되는지, codexShowUsageInMenuBar 설정이 저장소 재시작 뒤에도 유지되고 기본값이 표시 상태인지 확인한다. ‘Refresh’, ‘Copy Status Summary’, ‘Open codexreset.org’, ‘Quit’의 연결과 메뉴가 열린 동안 갱신 시 기존 항목을 교체하지 않고 수정하는지도 검사한다.

이 테스트 하네스는 NSMenu가 항목 동작을 처리할 수 있도록 NSApplication 객체만 만든다. run이나 finishLaunching을 호출하지 않으며 NSStatusItem을 만들거나 앱을 활성화하지 않는다. 창을 띄우거나 항목을 클릭하지 않고 앱 번들도 실행하지 않는다. 스크린샷, 손쉬운 사용 UI 검사, AppleScript, CGWindowList, open 명령도 사용하지 않는다.

따라서 이 테스트는 GUI 테스트가 아니다. 실제 메뉴 막대의 렌더링과 위치, 실제 상태 항목의 외관, 메뉴 클릭·추적 동작, 브라우저 열기, NSApplication을 통한 프로세스 종료는 테스트로 입증되지 않았다. 이런 주장은 데스크톱 상호작용이 필요하므로 검증된 것으로 취급하지 않는다.

Tests/BundleTests.sh는 AppIcon.icns 두 파일의 모든 표현과 iconutil로 추출한 모든 크기를 디코딩해 각각 투명하지 않은 픽셀이 있는지 확인한다. 또한 번들의 Info.plist 식별자·이름·패키지 유형·버전·최소 macOS 버전·LSUIElement·아이콘 선언, 복사된 아이콘, 포함된 LICENSE·NOTICE, 코드 서명을 검사한다.

메뉴 막대 아이콘 파일은 저장소에 포함된 그리기 스크립트로 생성한다.

sh
./Tools/generate-app-icon.swift Resources/AppIcon.icns

개인정보와 프로토콜

앱은 initialize, initialized, account/rateLimits/read만 전송한다. 응답의 accountId는 무시하며 저장·표시·기록하지 않는다. 검증 출력에는 사용량 제한 구간과 ordinaryUsageAllowed 불리언만 나타난다. 파서는 Codex app-server v2의 RateLimitSnapshot, RateLimitWindow, GetAccountRateLimitsResponse 유형을 따른다.

저장소와 앱 번들에는 분석 데이터, 계정, 토큰, 가져온 사이트 HTML, 로컬 사용량 데이터가 포함되지 않는다. .scratch/, build/, .build/, dist/, 인증 정보와 로컬 사용량 데이터는 Git에서 무시된다.

한계와 라이선스

이 프로젝트는 비공식 클라이언트다. codexreset.org는 공개 HTML 페이지를 데이터 원천으로 사용하며, 페이지 형식이 바뀌면 예측 파서를 업데이트해야 할 수 있다. 조기 복구 감지는 사이트 예측이나 소셜 게시물이 아니라 Codex 자체가 보고하는 사용량과 백엔드가 허용하는 상태에 달려 있다.

저장소에는 기여 안내, 행동 강령, 보안 정책, 변경 기록이 있다. MIT 라이선스로 배포하며, 저작자 표시와 상표 관련 안내는 NOTICE에 있다. Codex, OpenAI와 관련 명칭·표장은 각 소유자의 상표다. 이 프로젝트는 독립적으로 운영되며 OpenAI와 제휴하거나 OpenAI의 보증 또는 후원을 받지 않는다.