Friendly macOS sleep prevention for humans and AI agents.
Keep your machine awake for long builds, agent runs, and lunch breaks without remembering caffeinate flags. latte wraps it with a clean CLI and a live TUI dashboard.
$ latte status
☕ Active — preventing sleep
Modes: idle, disk, system (AC), wake display
Running for: 1h 23m
Remaining: 37m
curl -fsSL https://raw.githubusercontent.com/Junnyyy/latte/main/install.sh | bashRun the same command to update to the latest version. It will skip the download if you're already up to date.
Requires Bun.
git clone https://github.com/Junnyyy/latte.git
cd latte
bun installbun run linkbun run install:globalBoth methods install latte to ~/.local/bin/. Make sure it's in your PATH:
# Add to ~/.zshrc or ~/.bashrc if not already present
export PATH="$HOME/.local/bin:$PATH"bun run uninstallOr manually: rm ~/.local/bin/latte
latte onSpawns caffeinate -imsu in the background and returns immediately. Prevents idle sleep, disk sleep, and system sleep while letting the display sleep naturally.
latte offKills any running caffeinate process. Safe to run when nothing is running — exits cleanly.
latte 30m # 30 minutes
latte 2h # 2 hours
latte 90s # 90 secondsCaffeinate self-terminates when the timer expires. No cleanup needed.
latte statusShows whether caffeinate is running, which sleep modes are prevented, how long it's been active, and time remaining for timed sessions.
latteWhen run with no arguments in a terminal, launches a live TUI dashboard:
- o to start indefinite caffeinate
- x to stop caffeinate
- t to start a timed session (prompts for duration)
- q to quit (caffeinate keeps running)
The dashboard updates every second — you can watch a countdown tick in real time. When piped or in a non-TTY context, falls back to latte status.
If caffeinate is already running, latte on and latte <duration> will show what's active and ask before replacing it:
⚠ Caffeinate is already running (1h 23m, indefinite)
Kill existing and start 30m timer? (y/n)
In the TUI, this appears as a confirm dialog instead of a y/n prompt.
latte is a stateless wrapper around macOS caffeinate. It has no config files and no PID files — every command inspects running processes directly:
pgrep -x caffeinateto detect if caffeinate is runningpsto extract flags, start time, and timer durationpkill -x caffeinateto stop it
The flags are always -imsu:
| Flag | Prevents |
|---|---|
-i |
Idle sleep |
-m |
Disk sleep |
-s |
System sleep (on AC power) |
-u |
Declares user activity |
No -d (display). The display is free to sleep — latte is for keeping the machine awake, not the screen.
Every command is non-interactive when piped:
# Start before a long task
latte on
# Start with a timeout
latte 2h
# Check if running
latte status
# Clean up
latte off- Non-TTY mode uses plain text output (no TUI)
latte offis idempotent — safe to run unconditionally in scripts- Exit codes:
0success,1error
Built with Bun, Effect TS, @effect/cli, and Ink.
ProcessService Bun.spawn wrapper
|
+-- CaffeinateService detect, start, kill caffeinate
Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (latte --completions zsh).
bun install
bun run link # install as source symlink
bun run dev -- status # run directly without installing
bun test # run tests
bun run build # compile standalone binary
bun run typecheck # type checkRequires the GitHub CLI (gh).
bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.
MIT