Keeps the computer awake from Vicinae. Written against @vicinae/api directly because the Raycast `coffee` extension cannot work here: - Its `index` command is `menu-bar` mode, which Vicinae does not support. - Its `status` command crashes on `Cannot read properties of null (reading 'IsManuallyDecafed')` because it gates on `LocalStorage.getItem(key) === undefined`. Vicinae resolves a missing key to `null`, not `undefined` (verified with a runtime probe). Sleep prevention is an OS-level assertion, so the extension delegates and gets out of the way: - macOS: caffeinate -i [-d] [-s] [-t <seconds>] - Linux: systemd-inhibit --what=idle:sleep --why=<reason> sleep <n|infinity> The process is spawned detached + unref'd, because Vicinae unloads a no-view command as soon as its function returns. Timed sessions need no babysitter: `caffeinate -t` and `sleep <n>` self-terminate. Commands: toggle (no-view), status (view), caffeinate-for (view). Two deliberate safety choices: - Stopping signals the recorded PID rather than running `killall caffeinate`, so a caffeinate the user started themselves survives. Verified against a real concurrent `caffeinate -u -dmi` process. - The PID's name is re-checked with `ps -o comm=` before signalling, since PIDs get recycled and a stale entry could otherwise kill something unrelated. Also: display-sleep prevention is macOS-only, and the UI says so on Linux rather than pretending systemd-inhibit can hold the display awake. Verified: vici lint, tsc --noEmit, eslint, 23/23 vitest, vici build, and live in Vicinae — toggle starts `caffeinate -i` which survives the command unloading, toggle again stops only ours, both view commands load with clean stdout/stderr.
3.5 KiB
Caffeinate
Keep your computer awake from Vicinae. Start, stop, and time-limit sleep prevention without touching a 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 | Starts or stops sleep prevention, whichever is appropriate. |
| Caffeination Status | view | Live readout of whether sleep is being blocked, for how much longer, and the exact command holding it. Start/stop from the actions panel. |
| Caffeinate for a Duration | view | Keeps the computer awake for a set time, then lets it sleep again automatically. |
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] [-t <seconds>] |
| Linux | systemd-inhibit --what=idle:sleep --why=<reason> sleep <seconds|infinity> |
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. Timed sessions need no babysitter — caffeinate -t and sleep <seconds> self-terminate when the window elapses.
State is recorded in LocalStorage as { pid, startedAt, expiresAt, 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 (timed out, or killed elsewhere), 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. |
Duration formats
A bare number means minutes. Otherwise: 45m, 2h, 90s, 1d, 1h30m, or friendly words like 1 hour / 30 minutes.
The whole string must parse — 1h30 is rejected rather than silently read as 1h.
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
Not implemented
- Windows.
detectPlatformthrows with a clear message instead of failing obscurely.powercfgsupport would be the path. - While-an-app-is-running / scheduled windows.
caffeinate -w <pid>covers the former on macOS; neither is wired up. - A persistent menu bar indicator — Vicinae has no menu bar support. Use Caffeination Status instead.
License
MIT
