Caffeinate
Keep your computer awake from Vicinae. One toggle, no terminal.
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 process 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] (defaults to -i -d) |
| 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.
The process is the state
Nothing is persisted. The running process carries its own identity in its command line:
vicinae-caffeinate:v1:<startedAt>:<flags> e.g. vicinae-caffeinate:v1:1789895271765:id
- macOS has no free-form field on
caffeinate, so the marker is installed asargv[0]via the shell'sexec -a.execreplaces the shell in place, so the pid Node receives is thecaffeinatepid. - Linux passes it in
systemd-inhibit's--why, alongside a human-readable reason.
Status is therefore a ps scan for that marker, and stopping signals every match.
Why this design
The obvious approach — remember the pid in LocalStorage — has a failure mode that is worse than it sounds. Two pieces of state have to stay in sync: the record, and the actual process. If the record is ever lost (extension reinstalled, storage cleared, a crash between spawning and saving) the process keeps holding a sleep assertion that nothing will ever release. A laptop that silently never sleeps, with no way to find the culprit.
Making the process self-describing removes the class of bug rather than guarding against it: there is no record to lose, so the extension cannot lose track of a session. Any status or toggle reconciles against the truth, and start always clears existing sessions first so assertions cannot stack.
Verified by deleting the installed extension while a session was active, restarting Vicinae, then reinstalling: the fresh install saw the leftover as active and stopped it, instead of starting a second one and orphaning the first.
Signals go to the process group (kill -TERM -<pid>). Sessions are spawned detached, so the child is a group leader; on Linux that matters because systemd-inhibit forks a sleep, and signalling only the parent can leave the child behind.
A caffeinate you started yourself is never touched — only processes carrying the marker are ever signalled. If a session ever does need to be cleared by hand:
pkill -f vicinae-caffeinate
Preferences
| Preference | Default | Notes |
|---|---|---|
| Prevent Display Sleep | on | macOS only. On by default: caffeinate -i alone blocks system idle sleep, so the screen still blanks and the extension looks broken. Turn it off to save battery. 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.
detectPlatformthrows with a clear message instead of failing obscurely.powercfgwould 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
