Files
caffeinate-extension/README.md
T
adrianbonpin a303e161d3 Reduce to a pure on/off extension
Durations were the only thing keeping a lot of machinery alive, so drop
them and let the dead code go with them.

Removed:
- The "Caffeinate for a Duration" command.
- lib/duration.ts entirely (parseDuration / formatDuration /
  formatRemaining / normalizeDuration / DURATION_PRESETS) plus its test
  file. Every export was reachable only from duration paths.
- The 30s setInterval re-render in the status view. It existed only to
  keep a "27m left" countdown honest, and with no timed sessions there is
  nothing to count down.
- The duration-preset actions in the status view.
- describeOptions(), which only the duration form used to explain itself.
- durationSeconds / expiresAt from the options, state and command
  builder, along with the `-t <seconds>` and `sleep <n>` branches. Both
  platforms now hold the assertion until signalled, which is what "on"
  means here.
- The unused "restarted" member of ToggleResult.

The status view now reports "since <time>" instead of a countdown, and
keeps a Refresh action.

Also fixes a type error the narrower helper surfaced: the status view's
run() helper was typed () => Promise<void> while startCaffeination
returns Promise<CaffeinateState> and stopCaffeination returns
Promise<boolean>.

407 deletions against 57 insertions.

Verified: vici lint, tsc --noEmit, eslint, 8/8 vitest, vici build, and
live in Vicinae — toggle on spawns `caffeinate -i`, toggle off stops it,
and a pre-existing user-owned `caffeinate -u -dmi` is left untouched
throughout.
2026-09-20 17:01:30 +08:00

3.4 KiB

Caffeinate

Keep your computer awake from Vicinae. One toggle, no terminal.

Caffeinate icon

Why this exists

The Raycast coffee extension does not work on Vicinae. Its index command is a menu-bar command, which Vicinae cannot run at all, and its status command crashes because it gates on LocalStorage.getItem(key) === undefined — Vicinae resolves a missing key to null instead of undefined.

This is a native @vicinae/api extension, so none of that applies.

Commands

Command Mode What it does
Toggle Caffeination no-view Flips sleep prevention on or off, whichever is appropriate. The fast path.
Caffeination Status view Whether sleep is being blocked, since when, and the exact command holding it. Start/stop from here.

How it works

Sleep prevention is an OS-level assertion, so the extension delegates to the platform tool and then gets out of the way:

Platform Mechanism
macOS /usr/bin/caffeinate -i [-d] [-s]
Linux systemd-inhibit --what=idle:sleep --why=<reason> sleep infinity

Both run until signalled — there is no timer, so nothing needs to be refreshed or babysat.

The process is spawned detached and unref'd. That is required: Vicinae unloads a no-view command as soon as its function returns, so a child process would otherwise be torn down with it.

State is recorded in LocalStorage as { pid, startedAt, command, args, processName, … }.

Stopping signals the recorded PID, not killall caffeinate, so a caffeinate you started yourself in a terminal is left alone. Before signalling, the PID's process name is re-checked via ps -o comm= — PIDs get recycled, and without that check a stale entry could kill an unrelated process.

Status is self-healing: if the recorded process is gone (killed elsewhere, or a reboot), the stale state is cleared on the next read.

Preferences

Preference Default Notes
Also Prevent Display Sleep off macOS only. systemd-inhibit cannot hold the display awake — that is the compositor's job, and the UI says so rather than pretending.
Also Prevent System Sleep off On macOS this assertion only takes effect while on AC power.

Development

npm install
npm run dev        # vici develop — Vicinae must be running

Other scripts: npm run lint (manifest), npm run lint:code (ESLint), npm run typecheck, npm test.

To install a production build into Vicinae:

npm run build      # writes to ~/.local/share/vicinae/extensions/caffeinate

No top bar indicator

Vicinae has no menu-bar command support. This is not a Raycast-compat gap — a native mode: "menu-bar" command is accepted by the manifest and builds cleanly, then is silently dropped at registration, with no warning. @vicinae/api exports a MenuBarExtra component, but there is no host to render it into.

For a real top bar indicator you need a standalone macOS menu-bar app; it cannot be part of a Vicinae extension.

Not implemented

  • Windows. detectPlatform throws with a clear message instead of failing obscurely. powercfg would be the path.
  • Timed windows (caffeinate -t), while-an-app-is-running (caffeinate -w <pid>), and scheduled windows. Deliberately omitted — on/off covers the common case.

License

MIT