Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

All notable changes to Token Ghost are documented here. Versions follow [Semantic Versioning](https://semver.org).

## [Unreleased]

### Gemini
- Show Pro / Flash / Flash Lite daily quotas from Code Assist `retrieveUserQuota` using Gemini CLI Google-login credentials.
- Prefer macOS Keychain (`gemini-cli-oauth` / `main-account`) over legacy `oauth_creds.json`.
- Default: refresh OAuth access tokens **in memory only** (no Keychain mutation). Opt in with `TOKEN_GHOST_GEMINI_WRITEBACK=1`.
- Resolve OAuth client id/secret automatically from the installed `@google/gemini-cli` package (or env / creds fields) — no hard-coded client values.
- On refresh failure, do not reuse a stale/short access token; surface a re-login hint instead.
- Surface a clear setup hint when only API key / Vertex auth is configured.
- Add bun's `~/.bun/bin` to the PATH bootstrap so a bun-installed `gemini` is found under SwiftBar.

### Privacy / hardening
- Document Keychain vs file credential handling and write-back opt-in accurately; cache file mode `600` when possible.
- HTTP errors expose status codes only (no upstream bodies in menus / `--collect`).

### Docs
- Document Gemini data source, Google-login requirement, Keychain, env overrides, unofficial Claude TUI / Code Assist API limits, `v1.0.0` install pin vs Gemini branch, and Open Gemini menu action.

### Cleanup
- Remove unused severity helpers.
- Ignore `_workspace/` artifacts.

## [1.0.0] - 2026-07-10

First stable release.
Expand Down
42 changes: 31 additions & 11 deletions README.ko.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Token Ghost 👻

**Claude Code** **Codex**의 사용 한도를 macOS 메뉴바에서 한눈에 보여주는 작은 플러그인입니다.
**Claude Code**, **Codex**, **Gemini CLI**의 사용 한도를 macOS 메뉴바에서 한눈에 보여주는 작은 플러그인입니다.

Token Ghost는 [SwiftBar](https://swiftbar.app) 위에서 동작합니다. SwiftBar가 메뉴바 앱이고, Token Ghost는 그 안에서 Claude/Codex 사용량을 읽어 표시하는 플러그인입니다.
Token Ghost는 [SwiftBar](https://swiftbar.app) 위에서 동작합니다. SwiftBar가 메뉴바 앱이고, Token Ghost는 그 안에서 Claude/Codex/Gemini 사용량을 읽어 표시하는 플러그인입니다.

> 🇺🇸 English guide: [README.md](README.md)

Expand All @@ -24,6 +24,11 @@ Checked 3m ago
5 Hour: 🟩⬜️⬜️⬜️⬜️⬜️⬜️⬜️ 12% (~8:28pm)
Weekly: 🟩🟩🟩⬜️⬜️⬜️⬜️⬜️ 35% (~Jul 17 6am)
From session log, 40m ago

💜 Gemini
Pro: 🟩🟩⬜️⬜️⬜️⬜️⬜️⬜️ 28% (~3:00am)
Flash: 🟩⬜️⬜️⬜️⬜️⬜️⬜️⬜️ 12% (~3:00am)
From Code Assist, just now
```

프로그레스바는 사용률이 오를수록 초록 → 노랑 → 주황 → 빨강으로 채워집니다. 각 항목은 리셋 시각을 `(~시각)`으로 보여주고, 도구별로 값이 얼마나 최신인지도 표시합니다.
Expand All @@ -36,11 +41,11 @@ Token Ghost는 **독립 앱이 아니라** SwiftBar 플러그인입니다:

```text
SwiftBar = 메뉴바에 글자를 띄워주는 macOS 앱
Token Ghost = Claude/Codex 사용량을 읽어 메뉴에 표시하는 플러그인
Claude / Codex CLI = 사용 한도를 실제로 알고 있는 도구
Token Ghost = Claude/Codex/Gemini 사용량을 읽어 메뉴에 표시하는 플러그인
Claude / Codex / Gemini CLI = 사용 한도를 실제로 알고 있는 도구
```

즉, 터미널에서 Claude Code와 Codex가 이미 동작하는 상태여야 사용할 수 있습니다.
즉, 터미널에서 각 CLI가 이미 동작하는 상태여야 사용할 수 있습니다.

### 숫자는 어디서 오나요

Expand All @@ -51,6 +56,8 @@ Claude / Codex CLI = 사용 한도를 실제로 알고 있는 도구
- 메뉴에 데이터 나이(`From session log, 40m ago`)를 표시하고,
- 리셋 시각이 지난 윈도우는 `0%`와 함께 **다음 리셋 예상 시각**(보고된 리셋 시각에 창 길이를 더해 계산)을 표시합니다. 예: `5 Hour: 0% (~3:18am)`. 다음 Codex 실행이 새 값을 기록하면 실제 값으로 갱신됩니다.

**Gemini** — Gemini CLI를 **Google 계정으로 로그인**했을 때 생기는 OAuth로 Code Assist `retrieveUserQuota`를 호출합니다. 최신 Gemini CLI는 macOS Keychain(`gemini-cli-oauth` / `main-account`)에 두고, 예전 설치는 `~/.gemini/oauth_creds.json`을 쓸 수 있습니다. 결과는 Gemini CLI와 같이 **Pro / Flash / Flash Lite** 일일 버킷으로 합쳐 표시합니다. API 키·Vertex 인증은 로컬에 이 일일 바가 없어, `gemini`에서 `/auth` → **Login with Google**으로 바꿔야 합니다.

---

## 준비물
Expand All @@ -60,12 +67,14 @@ Claude / Codex CLI = 사용 한도를 실제로 알고 있는 도구
- [SwiftBar](https://swiftbar.app)
- Claude Code CLI 로그인 — Claude 사용량 표시에 필요
- Codex CLI 로그인 — Codex 사용량 표시에 필요
- Gemini CLI **Google 로그인** — Gemini Pro/Flash 일일 쿼터 표시에 필요

Token Ghost는 대신 로그인해주지 않습니다. 각 CLI가 이미 동작하는지 확인하세요:

```bash
codex login status # Codex
claude # Claude Code: TUI가 열리며, 필요하면 /login으로 로그인
gemini # Gemini: /auth → Login with Google (Keychain 또는 oauth_creds.json)
```

---
Expand All @@ -78,6 +87,8 @@ claude # Claude Code: TUI가 열리며, 필요하면 /login으로
curl -fsSL https://raw.githubusercontent.com/zoeymakes/token-ghost/v1.0.0/install.sh | bash
```

> **참고:** 핀된 `v1.0.0` 설치는 Claude + Codex만 포함합니다. Gemini 지원은 이 브랜치 / 다음 릴리스에 있습니다. Gemini가 필요하면 로컬 체크아웃에서 `./install.sh`를 쓰거나 Gemini가 포함된 태그를 기다리세요.

설치 스크립트 동작:

```text
Expand All @@ -104,7 +115,7 @@ SwiftBar가 자동으로 열립니다. **Plugin Folder**를 물어보면, 설치

- **Refresh** — 지금 즉시 새로 수집 (Claude 캐시 TTL 무시).
- **Setup guide** — 이 README 열기.
- **Open Claude / Open Codex** — 해당 앱 또는 웹사이트 열기.
- **Open Claude / Open Codex / Open Gemini** — 해당 앱 또는 웹사이트 열기.

---

Expand All @@ -116,6 +127,10 @@ SwiftBar가 자동으로 열립니다. **Plugin Folder**를 물어보면, 설치
| --- | --- | --- |
| `TOKEN_GHOST_CLAUDE_TTL_MIN` | `15` | Claude `/usage` 캡처 간격(분) |
| `TOKEN_GHOST_PLUGIN_DIR` | SwiftBar 설정 폴더 | 플러그인 설치 위치 |
| `TOKEN_GHOST_GEMINI_CLIENT_ID` / `TOKEN_GHOST_GEMINI_CLIENT_SECRET` | *(설치된 Gemini CLI에서 탐색)* | 토큰 갱신에만 쓰는 Gemini OAuth client 선택 오버라이드 |
| `GEMINI_OAUTH_CLIENT_ID` / `GEMINI_OAUTH_CLIENT_SECRET` | *(동일)* | 위와 같은 Gemini OAuth client 오버라이드 별칭 |
| `TOKEN_GHOST_GEMINI_WRITEBACK` | off | `1`이면 갱신된 OAuth 토큰을 Gemini CLI Keychain / `oauth_creds.json`에 다시 씀 (기본: 메모리만) |
| `TOKEN_GHOST_GEMINI_KEYCHAIN_SERVICE` / `TOKEN_GHOST_GEMINI_KEYCHAIN_ACCOUNT` | `gemini-cli-oauth` / `main-account` | 읽기·선택적 write-back에 쓰는 Keychain id |

---

Expand All @@ -131,11 +146,13 @@ SwiftBar가 자동으로 열립니다. **Plugin Folder**를 물어보면, 설치

## 개인정보

Token Ghost는 비밀번호, API Key, Access Token, 계정 인증정보를 **저장하지 않습니다**. 이미 설치된 `claude`, `codex` 도구에서 사용량만 읽어, 사용률과 리셋 시각만 아래에 캐시합니다:
Token Ghost는 **자체 비밀번호 저장소를 만들지 않습니다**. 하는 일은:

```text
~/.cache/token-ghost/cache.json
```
- 이미 설치된 `claude`, `codex`, `gemini` 도구에서 사용량만 읽고,
- 사용률·리셋 시각 등 비밀이 아닌 메타데이터만 `~/.cache/token-ghost/cache.json`에 캐시하며(가능하면 권한 `600`),
- 메뉴나 캐시에 access token·refresh token·API key를 출력하지 않습니다.

Gemini Google 로그인의 경우 access token이 만료되면 `retrieveUserQuota` 호출을 위해 **메모리에서만** 갱신합니다. 기본적으로 Gemini CLI Keychain이나 `oauth_creds.json`에는 다시 쓰지 않습니다(다른 앱 자격증명을 건드리지 않기 위함). 같은 저장소에 write-back이 필요하면 `TOKEN_GHOST_GEMINI_WRITEBACK=1`을 설정하세요.

---

Expand Down Expand Up @@ -168,7 +185,10 @@ curl -fsSL https://raw.githubusercontent.com/zoeymakes/token-ghost/main/uninstal

- 독립 `.app`이 아니라 SwiftBar 플러그인입니다.
- Codex 값은 Codex를 실제로 사용할 때만 갱신됩니다. 대신 데이터 나이를 표시하고, 리셋이 지난 윈도우는 예상 리셋 시각의 추정값으로 표시합니다.
- Claude 값은 짧은 인터랙티브 캡처로 얻으며, 캡처 사이(15분 TTL)에는 캐시된 값과 나이를 보여줍니다.
- Claude 값은 짧은 인터랙티브 `/usage` 캡처(Claude Code TUI 비공식 자동화)로 얻습니다. 첫 실행 시 workspace trust 프롬프트를 Enter로 자동 수락할 수 있으며, 캡처 사이(15분 TTL)에는 캐시된 값과 나이를 보여줍니다.
- Gemini Pro/Flash 바는 Gemini CLI **Google 계정 OAuth**(Keychain 또는 `oauth_creds.json`)가 필요합니다. API 키·Vertex 인증으로는 일일 버킷을 표시할 수 없습니다.
- Gemini 쿼터는 Google Code Assist `v1internal:retrieveUserQuota`(Gemini CLI가 쓰는 비공식에 가까운 API)로 가져옵니다. 예고 없이 바뀌거나 일부 개인 계정 등급을 거부할 수 있습니다.
- 일부 개인 Google 계정 등급은 Gemini CLI OAuth로 Code Assist 쿼터를 더 이상 받지 못할 수 있습니다. Google이 거부하면 메뉴에 설정 안내로 표시합니다.
- SwiftBar 메뉴는 텍스트 기반이라 프로그레스바는 컬러 사각형으로 표시합니다.

---
Expand Down
42 changes: 31 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Token Ghost 👻

A small macOS menu bar plugin that shows your **Claude Code** and **Codex** usage limits at a glance.
A small macOS menu bar plugin that shows your **Claude Code**, **Codex**, and **Gemini CLI** usage limits at a glance.

Token Ghost runs inside [SwiftBar](https://swiftbar.app). SwiftBar is the menu bar app; Token Ghost is the plugin that reads your Claude/Codex usage and renders it.
Token Ghost runs inside [SwiftBar](https://swiftbar.app). SwiftBar is the menu bar app; Token Ghost is the plugin that reads your Claude/Codex/Gemini usage and renders it.

> 🇰🇷 한국어 안내는 [README.ko.md](README.ko.md)를 참고하세요.

Expand All @@ -24,6 +24,11 @@ Checked 3m ago
5 Hour: 🟩⬜️⬜️⬜️⬜️⬜️⬜️⬜️ 12% (~8:28pm)
Weekly: 🟩🟩🟩⬜️⬜️⬜️⬜️⬜️ 35% (~Jul 17 6am)
From session log, 40m ago

💜 Gemini
Pro: 🟩🟩⬜️⬜️⬜️⬜️⬜️⬜️ 28% (~3:00am)
Flash: 🟩⬜️⬜️⬜️⬜️⬜️⬜️⬜️ 12% (~3:00am)
From Code Assist, just now
```

The bar fills green → yellow → orange → red as usage climbs. Every bucket shows its reset time as `(~time)`, and each tool shows how fresh its numbers are.
Expand All @@ -36,11 +41,11 @@ Token Ghost is **not a standalone app**. It is a plugin for SwiftBar:

```text
SwiftBar = the macOS menu bar app that displays text
Token Ghost = the plugin that reads Claude/Codex usage and prints the menu
Claude / Codex CLI = the tools that actually know your usage limits
Token Ghost = the plugin that reads Claude/Codex/Gemini usage and prints the menu
Claude / Codex / Gemini CLI = the tools that actually know your usage limits
```

So it only works once Claude Code and Codex already work in your terminal.
So it only works once those CLIs already work in your terminal.

### Where the numbers come from

Expand All @@ -51,6 +56,8 @@ So it only works once Claude Code and Codex already work in your terminal.
- the menu shows the data age (`From session log, 40m ago`), and
- once a window's reset time has passed, the bucket shows `0%` with the **projected next reset** (the reported reset rolled forward by the window length), e.g. `5 Hour: 0% (~3:18am)`, until the next Codex run writes fresh numbers.

**Gemini** — read from Google Code Assist `retrieveUserQuota` using Gemini CLI Google-login credentials. On current Gemini CLI builds those live in the macOS Keychain (`gemini-cli-oauth` / `main-account`); older installs may still use `~/.gemini/oauth_creds.json`. Buckets are collapsed into **Pro / Flash / Flash Lite** (same grouping as Gemini CLI's model usage UI). API key and Vertex auth do **not** expose those local daily bars — switch to **Login with Google** via `/auth` inside `gemini`.

---

## Requirements
Expand All @@ -60,12 +67,14 @@ So it only works once Claude Code and Codex already work in your terminal.
- [SwiftBar](https://swiftbar.app)
- Claude Code CLI, logged in — required for Claude usage
- Codex CLI, logged in — required for Codex usage
- Gemini CLI, logged in with **Google** — required for Gemini Pro/Flash daily quotas

Token Ghost never logs in for you. Check that each CLI already works:

```bash
codex login status # Codex
claude # Claude Code: opens the TUI; log in with /login if asked
gemini # Gemini: /auth → Login with Google (Keychain or oauth_creds.json)
```

---
Expand All @@ -78,6 +87,8 @@ Paste this into Terminal (no angle brackets):
curl -fsSL https://raw.githubusercontent.com/zoeymakes/token-ghost/v1.0.0/install.sh | bash
```

> **Note:** The pinned `v1.0.0` install is Claude + Codex only. Gemini support is in this branch / the next release — install from a local checkout (`./install.sh`) or wait for a tagged release that includes Gemini.

What the installer does:

```text
Expand All @@ -104,7 +115,7 @@ SwiftBar opens automatically. If it asks for a **Plugin Folder**, the installer

- **Refresh** — force a fresh capture now (bypasses the Claude cache TTL).
- **Setup guide** — open this README.
- **Open Claude / Open Codex** — launch the respective app or website.
- **Open Claude / Open Codex / Open Gemini** — launch the respective app or website.

---

Expand All @@ -116,6 +127,10 @@ Environment variables (set them where SwiftBar can see them, or edit the plugin)
| --- | --- | --- |
| `TOKEN_GHOST_CLAUDE_TTL_MIN` | `15` | Minutes between Claude `/usage` captures |
| `TOKEN_GHOST_PLUGIN_DIR` | SwiftBar's configured folder | Where the plugin is installed |
| `TOKEN_GHOST_GEMINI_CLIENT_ID` / `TOKEN_GHOST_GEMINI_CLIENT_SECRET` | *(from installed Gemini CLI)* | Optional override for Gemini OAuth client used only when refreshing tokens |
| `GEMINI_OAUTH_CLIENT_ID` / `GEMINI_OAUTH_CLIENT_SECRET` | *(same)* | Alternate names for the same Gemini OAuth client override |
| `TOKEN_GHOST_GEMINI_WRITEBACK` | off | Set to `1` to write refreshed OAuth tokens back into Gemini CLI Keychain / `oauth_creds.json` (default: in-memory only) |
| `TOKEN_GHOST_GEMINI_KEYCHAIN_SERVICE` / `TOKEN_GHOST_GEMINI_KEYCHAIN_ACCOUNT` | `gemini-cli-oauth` / `main-account` | Keychain ids used for both read and optional write-back |

---

Expand All @@ -131,11 +146,13 @@ Environment variables (set them where SwiftBar can see them, or edit the plugin)

## Privacy

Token Ghost does **not** store passwords, API keys, access tokens, or account credentials. It reads usage from the already-installed `claude` and `codex` tools and caches only usage percentages and reset times at:
Token Ghost does **not** invent its own password store. It:

```text
~/.cache/token-ghost/cache.json
```
- reads usage from the already-installed `claude`, `codex`, and `gemini` tooling,
- caches only usage percentages, reset times, and similar non-secret metadata at `~/.cache/token-ghost/cache.json` (mode `600` when the filesystem allows),
- never prints access tokens, refresh tokens, or API keys in the menu or cache.

For Gemini Google login Token Ghost may **refresh** an expired OAuth access token in memory to call `retrieveUserQuota`. By default it does **not** write those refreshed tokens back into Gemini CLI's Keychain or `oauth_creds.json` (avoids mutating another app's credentials). Set `TOKEN_GHOST_GEMINI_WRITEBACK=1` if you want write-back to the same store the credentials were loaded from.

---

Expand Down Expand Up @@ -168,7 +185,10 @@ To remove the local cache too, from a downloaded copy:

- Token Ghost is a SwiftBar plugin, not a standalone `.app`.
- Codex numbers move only when Codex actually runs; the menu shows the data age and marks passed reset windows as projected estimates.
- Claude usage comes from a short interactive capture; between captures (15 min TTL) the menu shows the cached value with its age.
- Claude usage comes from a short interactive `/usage` capture (unofficial automation of the Claude Code TUI). On first launch that capture may auto-accept Claude's workspace-trust prompt with Enter; between captures (15 min TTL) the menu shows the cached value with its age.
- Gemini Pro/Flash bars require Gemini CLI **Google account** OAuth (Keychain or `oauth_creds.json`). API key / Vertex auth cannot show those daily buckets.
- Gemini quota is fetched via Google Code Assist's `v1internal:retrieveUserQuota` endpoint — the same private-ish API Gemini CLI uses. It can change or reject some personal account tiers without notice.
- Some personal Google account tiers may no longer receive Code Assist quota through Gemini CLI OAuth; Token Ghost surfaces that as a setup error when Google returns it.
- SwiftBar menus are text, so progress bars are drawn with colored squares.

---
Expand Down
20 changes: 20 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Security Policy

## What Token Ghost touches

Token Ghost is a local SwiftBar plugin. It never sends your prompts or project files to a Token Ghost server (there isn't one).

| Surface | Behavior |
| --- | --- |
| Claude | Spawns a short local `claude` TUI session to capture `/usage`. May auto-accept the first-run workspace trust dialog with Enter. |
| Codex | Reads local session logs under `~/.codex/sessions/`. |
| Gemini | Reads Google OAuth tokens that Gemini CLI already stored (Keychain `gemini-cli-oauth` or `~/.gemini/oauth_creds.json`), may refresh the access token **in memory** to call Google Code Assist `retrieveUserQuota`. By default it does **not** write refreshed tokens back into Gemini CLI stores (`TOKEN_GHOST_GEMINI_WRITEBACK=1` opts in). |
| Cache | Writes usage percentages / reset times to `~/.cache/token-ghost/cache.json` (mode `600` when possible). |

OAuth client id/secret used for Gemini token refresh are discovered from the installed `@google/gemini-cli` package or optional environment overrides — they are not hard-coded in this repository.

## Reporting a vulnerability

Please open a private GitHub security advisory on the repository you cloned from, or email the maintainer listed on that repository. Include steps to reproduce and whether credentials or tokens can be exposed.

Do not file public issues that include access tokens, refresh tokens, API keys, or keychain dumps.
Loading