Skip to content

Repository files navigation

The Guaranate guaraná berry mascot

Guaranate

A developer-friendly macOS keep-awake CLI.

Guaranate is a small, native macOS command-line utility for managing sleep-prevention sessions with a friendlier, more scriptable interface than caffeinate.

It talks to the native macOS power-management APIs (IOKit IOPMAssertion*) directly. It does not wrap /usr/bin/caffeinate.

The name is a play on guaraná, the Brazilian stimulant, and Apple's caffeinate command.

Keep your Mac awake when work needs to finish. Give your Mac some guaraná.


Status

v0.1 — native CLI foundation. Timed sessions and process-lifetime sessions work end-to-end. The broader surface (until, status, why, external leases) is planned and tracked in PLAN.md. Rows marked unreleased are on main and ship in the next release.

Feature State
guaranate <duration> timed session ✅ shipped
guaranate (no duration) runs until interrupted ✅ shipped
Native IOPMAssertion (no caffeinate) ✅ shipped
Elapsed / remaining / end-time display ✅ shipped
Ctrl+C / SIGTERM cleanup, no stale assertion ✅ shipped
Non-TTY-friendly output ✅ shipped
guaranate while <cmd> for a command's lifetime ✅ unreleased
guaranate --watch <pid> for a running process ✅ unreleased
guaranate until <HH:MM> 🔜 v0.2
status / why / --json 🔜 v0.3
acquire / renew / release leases 🔜 v0.4

Documentation

The Guaranate mascot inside a terminal window

This README is the quickstart. The full user guide — install, session options, assertion modes, terminal output, and a CLI reference generated from the binary's own help output — is published at guaranate.dev.

Its source lives in docs-website/, a self-contained Astro Starlight site deployed with Zephyr Cloud; every build also gets its own immutable preview URL.

cd docs-website && npm install && npm run dev

Install

With Homebrew

brew tap ryok90/guaranate
brew install guaranate

From a release binary

Download the universal (arm64 + x86_64) build from the latest release, verify its checksum, and put it on your PATH:

shasum -a 256 -c guaranate-*-macos-universal.tar.gz.sha256
tar -xzf guaranate-*-macos-universal.tar.gz
cp guaranate-*-macos-universal/guaranate /usr/local/bin/   # or anywhere on your PATH

From source

Requires Swift 6 (Xcode 16+) on macOS 14 or later.

git clone https://github.com/ryok90/guaranate.git
cd guaranate
swift build -c release
cp .build/release/guaranate /usr/local/bin/   # or anywhere on your PATH

Usage

Keep the Mac awake for a human-readable duration:

guaranate 30m
guaranate 2h
guaranate 1h30m
guaranate 90s
guaranate 3600      # a bare integer is interpreted as seconds
guaranate           # no duration: stay awake until interrupted (Ctrl+C)

While active, Guaranate acquires a power assertion and shows a live frame. On a color terminal it renders a gradient progress bar, a metrics table, and a centered header, sized to your terminal width:

             🌿 Guaranate

  ▏██████████████░░░░░░░░░░░░░░▕   50%

  Elapsed       · · · · · · · 00:42:17
  Remaining     · · · · · · · 01:17:43
  Ends          · · · · · · · 23:43:07
  Assertion     · · · · · System sleep
  Display       · · · · · ·  May sleep

Press Ctrl+C or q to stop

The bar is drawn with a green→berry-red gradient (truecolor or 256-color, degrading to solid green) and sizes itself to the terminal. Assertion state is color-coded (amber when the display is kept awake), the cursor is hidden while the frame is live, and a summary card is shown on completion. Running with no duration stays awake indefinitely; that frame swaps the progress bar for a spinner (⠋ Awake — until interrupted). Color and Unicode degrade independently: NO_COLOR drops color, and a dumb or non-UTF-8 terminal falls back to a plain ASCII bar ([####----]) with no escape codes.

Press q or Ctrl+C to end a live session; it is also released automatically when the duration elapses or on SIGTERM — never leaving a stale sleep inhibitor behind.

Command and process lifetimes

Hold the assertion for exactly as long as a command runs:

guaranate while npm test
guaranate while ./build.sh --release
guaranate while --display -- ./deploy.sh   # flags go before the command

A mistyped flag before the command is reported as an unknown option rather than run as a program; -- is required for a program whose own name starts with -.

The command gets its own process group and the controlling terminal — its output and input pass straight through, so there is no live frame, just a start line and a completion summary — and Guaranate exits with the command's own exit code (128 + signal if it is killed by one, 127 if the command is not found, 126 if it is not executable). Ctrl+C and Ctrl+Z behave exactly as they would without Guaranate in front: an interrupt arrives once, and Ctrl+Z stops the whole job for fg to resume, assertion held while it is paused. Signals sent to Guaranate are relayed to the command's whole process group, so its children are signalled with it rather than left behind, and the assertion is released only once the command has exited — a stopped session relays when it is continued, which fg, bg, your shell's kill %job and a closing terminal all do for you; a raw signal to a paused session waits for that continue, exactly as it would for any stopped process. Resuming re-decides the terminal, so a job put in the background with bg leaves your shell's keyboard alone. A signal the calling shell deliberately ignores stays ignored in the command, too. Guaranate's own start and completion lines go to stderr, so stdout carries the command's output alone and while is safe in a pipeline: a reader that goes away reaches the command exactly as it would unwrapped, and never the session.

Hold the assertion until an already-running process exits:

guaranate --watch 4821
guaranate -w 4821

Watching only observes: the process is never started, signaled, or killed, and Ctrl+C detaches and leaves it running. It works for processes owned by other users, is bound to the process's (pid, start time) pair so a recycled pid can never inherit the assertion, and attributes the assertion to the watched process — pmset -g assertions reports Created for PID: 4821. An unused pid is rejected before anything is acquired, and --watch cannot be combined with a duration.

Assertion modes

The default prevents user-idle system sleep while still letting the display sleep — the right mode for builds, downloads, and unattended compute.

guaranate 2h              # prevent idle system sleep (display may sleep)
guaranate 2h --display    # also keep the display awake
guaranate 2h --system     # prevent all system sleep

Options

Option Description
-d, --display Also keep the display awake.
-s, --system Prevent all system sleep.
-r, --reason <text> Reason recorded on the power assertion.
-w, --watch <pid> Hold the assertion until that process exits.
-v, --version Print the version.
-h, --help Show help.

Non-interactive output

When stdout is not a TTY (pipes, files, CI), the live frame degrades to a single start line and a single completion line — no per-second churn.


How it works

Guaranate uses IOKit power assertions directly:

  • IOPMAssertionCreateWithName to acquire an assertion of the requested type (PreventUserIdleSystemSleep, PreventUserIdleDisplaySleep, or PreventSystemSleep);
  • IOPMAssertionRelease on every exit path.

Apple's open-source caffeinate.c is treated only as an engineering reference for assertion semantics — no Apple implementation code is copied, and /usr/bin/caffeinate is never invoked.

You can confirm a live assertion with:

pmset -g assertions

Architecture

The important boundary is CLI → GuaranateCore → macOS power APIs. A future menu-bar companion would depend on GuaranateCore, not the CLI executable.

Sources/
├── GuaranateCLI/            # commands, terminal rendering, process runtime
│   ├── Guaranate.swift      # @main root command, subcommand container
│   ├── TimedSession.swift   # timed / --watch session: render loop, release
│   ├── ProcessSession.swift # while: spawn, forward signals, propagate exit
│   ├── Commands/            # AssertionOptions, RunCommand, WhileCommand
│   └── Output/
│       └── TerminalRenderer.swift
└── GuaranateCore/           # pure, IOKit-free logic (unit-tested)
    ├── Power/               # PowerAsserting protocol + IOKit PowerManager
    ├── Process/             # ChildProcess, CommandInvocation, ExitStatus,
    │                        # ProcessIdentity
    ├── Time/                # DurationParser, Deadline, TimeFormatting
    └── Terminal/            # ProgressBar (pure bar math for the live frame)

Native IOKit interaction sits behind the PowerAsserting protocol, so time, duration, and (later) lease logic are fully testable without changing the host machine's sleep state.


Development

swift build      # build
swift test       # run the unit tests
swift run guaranate 10m

See CHANGELOG.md for notable changes and AGENTS.md for repository conventions. Every pull request with user-facing impact adds an entry under ## [Unreleased] in CHANGELOG.md; CI enforces this.


License

MIT. Guaranate's implementation is original.

About

A developer-friendly, native macOS keep-awake CLI

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages