Skip to content
Merged
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
12 changes: 7 additions & 5 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Depending on the providers you enable, `limitping` may read:

- Claude Code OAuth credentials from the macOS Keychain or
`~/.claude/.credentials.json`
- Codex OAuth credentials from `~/.codex/auth.json` or `$CODEX_HOME/auth.json`
- Codex/Spark OAuth credentials from `~/.codex/auth.json` or
`$CODEX_HOME/auth.json`
- Your `limitping` configuration from `~/.config/limitping/config.toml`
- Provider usage responses used to calculate reset times

Expand All @@ -18,17 +19,18 @@ The tool may write:
- `~/.config/limitping/config.toml` when you run `limitping config init`
- `~/.config/limitping/litellm_prices.json`, a cached copy of the LiteLLM pricing
dataset used for Codex cost estimates
- Rotated Claude/Codex OAuth tokens back to the same credential stores used by
the official CLIs, when a refresh is required
- Rotated Claude/Codex/Spark OAuth tokens back to the same credential stores
used by the official CLIs, when a refresh is required

## Network Requests

`limitping` makes network requests only to support the command you run:

- Anthropic Claude Code OAuth and usage endpoints
- ChatGPT/Codex OAuth and usage endpoints
- ChatGPT/Codex/Spark OAuth and usage endpoints
- GitHub releases, when using `install.sh`
- The LiteLLM pricing dataset on GitHub, for Codex equivalent API-cost estimates
- The LiteLLM pricing dataset on GitHub, for Codex/Spark equivalent API-cost
estimates

The tool does not send provider credentials to unrelated services.

Expand Down
80 changes: 50 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
![Go](https://img.shields.io/badge/Go-1.25%2B-00ADD8?logo=go&logoColor=white)
![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey)

Start the next **Claude Code** or **Codex** rate-limit window the moment the
previous one resets.
Start the next **Claude Code**, **Codex**, or **Spark** rate-limit window the
moment the previous one resets.

Claude Code and Codex subscription limits run on **5-hour rolling windows**
(plus a weekly cap). A fresh 5h window does not start just because the previous
one reset; it starts when you send the first billable request. If that happens
hours later, the gap is wasted and your window schedule drifts.
Claude Code, Codex, and Spark subscription limits run on **5-hour rolling
windows** (plus a weekly cap). A fresh 5h window does not start just because the
previous one reset; it starts when you send the first billable request. If that
happens hours later, the gap is wasted and your window schedule drifts.

`limitping` watches the reset time and sends one tiny request through the
official provider CLI right after rollover. Run it once, keep `watch` in the
Expand All @@ -28,6 +28,7 @@ after the terminal closes.
```
claude ✓ pinged (6.6s)
codex ✓ pinged (13.6s)
spark ✓ pinged (12.4s)
```

## Highlights
Expand All @@ -37,10 +38,10 @@ codex ✓ pinged (13.6s)
`bg start` with `bg status`, `bg logs -f`, and `bg stop`.
- Shows 5h and weekly usage, reset countdowns, and background watcher state from
read-only usage endpoints.
- Triggers Claude Code and Codex through their official CLIs using your existing
logged-in credentials.
- Detects active Claude/Codex turns via CLI hooks and defers its own ping so it
does not compete with your work.
- Triggers Claude Code, Codex, and Spark through their official CLIs using your
existing logged-in credentials.
- Detects active Claude/Codex turns via CLI hooks; Spark uses the Codex hook
signal because it runs through the Codex CLI.
- Includes dry-run modes, weekly-limit guards, reset buffers, cheap-model
defaults, macOS notifications, local config, and no telemetry.

Expand Down Expand Up @@ -68,6 +69,7 @@ provider quota: `limitping ping --dry-run`, `limitping watch --dry-run`, or
|---|---|---|---|
| **Claude Code** | `…/api/oauth/usage` | interactive Claude Code CLI | OAuth (Keychain / `~/.claude`) |
| **Codex** | `…/backend-api/wham/usage` | interactive Codex CLI | OAuth (`~/.codex/auth.json`) |
| **Spark** | `…/backend-api/wham/usage` (`additional_rate_limits`) | interactive Codex CLI with `gpt-5.3-codex-spark` | OAuth (`~/.codex/auth.json`) |

## How it works

Expand All @@ -81,10 +83,10 @@ Two cleanly separated jobs:
When `watch` sees a 5h window has reset, it first checks whether a Claude/Codex
session is actively mid-turn. If one is, `limitping` waits and re-reads usage
instead of sending its own ping, because that session's next model request will
start the new window naturally. This check relies on the
[CLI hooks](#active-session-detection-hooks) (installed automatically by the
install script); without them, `limitping` skips the check and pings as soon as
the window resets.
start the new window naturally. Spark uses the Codex activity signal. This check
relies on the [CLI hooks](#active-session-detection-hooks) (installed
automatically by the install script); without them, `limitping` skips the check
and pings as soon as the window resets.

- **Claude**: reads `GET https://api.anthropic.com/api/oauth/usage` using the
OAuth token from the macOS Keychain (`Claude Code-credentials`) or
Expand All @@ -96,9 +98,13 @@ the window resets.
OAuth token from `~/.codex/auth.json`. Triggering uses a TTY-backed
interactive `codex "<prompt>"` session; headless `codex exec` can consume
tokens without anchoring the subscription-backed Codex window.
- **Spark**: uses the same Codex usage endpoint, OAuth token, hooks, and
interactive CLI path. It reads the `GPT-5.3-Codex-Spark` entry from
`additional_rate_limits`, sends the ping with model `gpt-5.3-codex-spark`,
and appears as a separate `spark` provider.

Claude/Codex tokens are reused from the official tools (no separate login) and
refreshed on 401.
refreshed on 401. Spark reuses the Codex token.

## Install

Expand Down Expand Up @@ -154,7 +160,7 @@ go build -o bin/limitping ./cmd/limitping
```

Each provider you enable needs its own credentials: the `claude` / `codex` CLIs
logged in.
logged in. Spark uses the Codex CLI credentials.

## Usage

Expand All @@ -166,9 +172,10 @@ limitping status -v # also print raw JSON
limitping ping # trigger all enabled providers now (alias: p)
limitping ping claude # Claude only
limitping ping codex # Codex only
limitping ping spark # Spark only
limitping ping --dry-run # show the commands without sending
limitping watch # foreground daemon: ping each window at reset (alias: w)
limitping watch claude # watch only one provider (claude|codex)
limitping watch claude # watch only one provider (claude|codex|spark)
limitping watch --live # optional live heartbeat/status line
limitping watch --dry-run # log when pings would fire, without sending
limitping bg start # run watch in the background, freeing the terminal
Expand Down Expand Up @@ -203,7 +210,7 @@ Short aliases are also available for config commands: `limitping c i` for
| `uninstall` | `rm`, `remove` |

`ping` shows the exact command and a live timer (a spinner on a terminal).
Current Claude/Codex interactive trigger sessions do not expose reliable
Current Claude/Codex/Spark interactive trigger sessions do not expose reliable
machine-readable per-ping token or cost data, so success output normally shows
elapsed time only:

Expand All @@ -212,6 +219,8 @@ claude → claude --model haiku .
claude ✓ pinged (6.6s)
codex → codex -c model_reasoning_effort=low -m gpt-5.4-mini ok
codex ✓ pinged (13.6s)
spark → codex -c model_reasoning_effort=low -m gpt-5.3-codex-spark ok
spark ✓ pinged (12.4s)
```

Use `status` or `bg status` for the authoritative 5h/weekly window view after a
Expand Down Expand Up @@ -284,6 +293,14 @@ model = "gpt-5.4-mini" # cheapest Codex model for triggering
reasoning_effort = "low" # "minimal" is rejected when web_search/image_gen tools are enabled
extra_args = [] # extra Codex CLI args; exec-only flags such as --json are ignored
align_start = ""

[spark]
enabled = false # opt in; Spark is a separate Codex-backed watch target
prompt = "ok"
model = "gpt-5.3-codex-spark"
reasoning_effort = "low"
extra_args = []
align_start = ""
```

Top-level keys:
Expand All @@ -305,11 +322,13 @@ your budget:
- **Claude → `haiku`**: also avoids the separate weekly Opus bucket.
- **Codex → `gpt-5.4-mini`**: the mini variant (see `~/.codex/models_cache.json`
for what your plan offers).
- **Spark → `gpt-5.3-codex-spark`**: a Codex-backed Spark target, disabled by
default so upgrades do not add another quota-consuming ping.

Claude/Codex don't expose per-model prices at runtime (Anthropic's local cost
cache is empty; Codex's model cache has no price field), so the cheapest model is
a sensible default rather than a live price lookup. Override `model` per provider
if you prefer.
Claude/Codex/Spark don't expose per-model prices at runtime (Anthropic's local
cost cache is empty; Codex's model cache has no price field), so the cheapest
model is a sensible default rather than a live price lookup. Override `model`
per provider if you prefer.

### Active-session detection (hooks)

Expand All @@ -330,7 +349,8 @@ This registers limitping's hooks in `~/.claude/settings.json` and
written). The hooks invoke the hidden `limitping hook <provider>` command on
`UserPromptSubmit` / `PreToolUse` / `PostToolUse` / `Stop` (Claude also
`SessionEnd`) to record whether a session is mid-turn under
`~/.config/limitping/activity/`.
`~/.config/limitping/activity/`. Spark runs through the Codex CLI and uses the
Codex hook/activity marker; there is no separate Spark hook config.

> [!NOTE]
> Claude Code loads its hooks automatically — nothing to do there. **Codex**
Expand Down Expand Up @@ -393,17 +413,17 @@ launchctl load ~/Library/LaunchAgents/com.limitping.watch.plist
uses a minimal prompt and low reasoning, so the cost is tiny but non-zero.
- The **usage endpoints are unofficial** and could change; they're read-only and
isolated per provider for easy patching.
- macOS-first: Keychain reads and notifications are macOS-only. Codex `auth.json`
is cross-platform; Claude on Linux uses `~/.claude/.credentials.json`;
notifications are a no-op off macOS.
- macOS-first: Keychain reads and notifications are macOS-only. Codex/Spark
`auth.json` is cross-platform; Claude on Linux uses
`~/.claude/.credentials.json`; notifications are a no-op off macOS.

## Layout

```
cmd/limitping CLI entry
internal/config TOML config
internal/usage normalized usage model
internal/auth Claude (Keychain) + Codex (auth.json) tokens
internal/auth Claude (Keychain) + Codex/Spark (auth.json) tokens
internal/provider per-provider ReadUsage (endpoint) + Trigger (CLI)
internal/activity hook-based active-session state (shared by the hook cmd + scheduler)
internal/pricing pricing helpers for providers that expose token usage
Expand All @@ -424,9 +444,9 @@ go vet ./...
go test ./...
```

Providers are isolated in `internal/provider` (one file each) with a small
`Provider` interface (`ReadUsage` + `Trigger`), so adding a new provider is
mostly a self-contained file plus wiring in `internal/cli` and `internal/config`.
Providers are isolated in `internal/provider` behind a small `Provider`
interface (`ReadUsage` + `Trigger`), so adding a new provider is mostly
self-contained provider code plus wiring in `internal/cli` and `internal/config`.

**Releasing** is automated: push a tag and GitHub Actions runs GoReleaser to
build the cross-platform binaries and publish a Release.
Expand Down
Loading