`caffeinate -i` blocks system idle sleep but lets the display blank, so the screen still slept and the extension looked broken. The "Also Prevent Display Sleep" preference that fixes this defaulted to off. Flip it to on, and guard it with a manifest test that reads package.json and asserts the shipped defaults map to `-i -d`. Verified on macOS: `caffeinate -i` yields PreventUserIdleDisplaySleep 0, `caffeinate -i -d` yields 1.
5.1 KiB
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
