Kick Claude Code and Codex at a fixed time every morning, even while your Mac is asleep.
Claude Code's quota resets every 5 hours, starting from your first usage of the day. By sending a minimal request at a fixed early hour (e.g. 07:00), you anchor the reset cycle to predictable windows:
| Window | Reset at | |
|---|---|---|
| Morning | 07:00 | Start fresh |
| Afternoon | 12:00 | Back from lunch |
| Evening | 17:00 | One more round |
Without this anchor, the cycle drifts based on whenever you happen to start. quota-beat wakes your Mac and fires that first request automatically, even while asleep.
- macOS (depends on
launchdandpmset) - Node.js >= 18
- Claude Code CLI and/or Codex CLI installed and authenticated
sudoaccess (required forpmset repeat wakeorpoweron)
npm install -g @yesongh/quota-beatThis package installs two command aliases: qbeat (recommended) and quotabeat.
npm install -g @yesongh/quota-beat@latest# Schedule a daily kick at 07:00 (default)
qbeat install
# Or pick your own time
qbeat install --time 06:00
# Verify
qbeat statusShow the installed qbeat version and exit.
Register a daily launchd job and a pmset wake schedule.
- Time must be in 24-hour
HH:MMformat. Defaults to07:00. - Running
installagain replaces the existing schedule. - Requires
sudoforpmset.
Show the configured daily time. Reads directly from the installed launchd plist — no state file involved.
Run a kick for all available providers (Claude Code, Codex) immediately. Does not modify any schedule.
Remove the launchd job and all quota-beat-owned pmset wake entries. Does not remove the globally installed qbeat binaries.
When you run qbeat in an interactive terminal, it may check npm for a newer published version about once per day.
If one is available, qbeat offers to run npm install -g @yesongh/quota-beat@<resolved-version> for you.
qbeat --version only prints the installed version and does not perform this check.
The background scheduled path never performs update checks or prompts.
Implementation notes and rollout pitfalls are documented in docs/self-update-pattern.md.
┌─────────────────────────────────────────────────────┐
│ pmset repeat wakeorpoweron (configured time − 1 min)│
│ ↓ Mac wakes from sleep │
│ launchd fires the scheduled quota-beat job │
│ ↓ │
│ 1. Wait for network (up to 30s) │
│ 2. Kick all available providers (Claude Code, Codex)│
│ 3. Append kick attempt log │
└─────────────────────────────────────────────────────┘
pmset repeat wakeorpoweronwakes your Mac 1 minute before each configured kick time every day.launchdtriggers the installed quota-beat job at the exact configured time.- The tool checks network connectivity (DNS lookup to
api.anthropic.comandapi.openai.com, retries for up to 30 seconds). - A minimal request is sent to each available provider — Claude Code (
claude -p "Reply with exactly OK.", using your configured default model) and/or Codex (codex exec --ephemeral --skip-git-repo-check -c model_reasoning_effort=low "Reply with exactly OK.") — to activate the quota. - Each kick attempt is appended to
~/.quota-beat/logs/kick.jsonlfor later inspection.
Five zero-dependency modules:
| Module | Responsibility |
|---|---|
src/cli.mjs |
Command routing, argument parsing, install/status/kick/uninstall flow |
src/help.mjs |
Root help text, command help text, and usage hints |
src/update.mjs |
Interactive npm version checks, cache management, prompting, and self-update |
src/scheduler.mjs |
launchd plist generation & parsing, pmset wake scheduling & cleanup |
src/kick.mjs |
Provider definitions, network readiness check, CLI execution (Claude Code, Codex) |
Key design decisions:
- Absolute Node path in plist —
launchdruns with a minimalPATHand can't reliably find user-managed Node installations. The plist embeds the absoluteprocess.execPathcaptured at install time. - Scoped pmset cleanup — quota-beat cancels only the
wakeorpoweronrepeat rule it manages, avoiding broaderpmsetresets. - No state file —
statusreads the installed plist as the single source of truth.
See docs/architecture.md for detailed execution flows.
launchd stdout/stderr logs are written to:
~/.quota-beat/logs/launchd.stdout.log
~/.quota-beat/logs/launchd.stderr.log
Each provider kick attempt is also appended as one JSON record per line to:
~/.quota-beat/logs/kick.jsonl
Each entry includes a provider field (claude or codex), whether the invocation succeeded, its exit code, and short stdout/stderr previews.
qbeat status says "Not installed"
Run qbeat install --time HH:MM again.
Upgrade to the latest published build
Run npm install -g @yesongh/quota-beat@latest.
Node path changed (e.g. after nvm switch)
Re-run qbeat install --time HH:MM to capture the new process.execPath.
pmset requires sudo
install and uninstall need sudo to manage wake schedules. Run sudo -v first or use a passwordless sudoers entry for pmset.
Do not run qbeat install with sudo
Run qbeat install --time HH:MM as your normal login user. qbeat handles the internal sudo pmset ... step itself, and launchd registration must stay in your user gui/<uid> domain.
Verify the launchd job is loaded
launchctl print gui/$(id -u)/com.quota-beat.kickVerify pmset wake schedule
pmset -g sched# Run tests
npm test
# Full macOS verification
sudo -v
node bin/qbeat.mjs install --time 07:00
node bin/qbeat.mjs status
pmset -g sched
launchctl print gui/$(id -u)/com.quota-beat.kick
node bin/qbeat.mjs uninstallMIT