adrianbonpin 65005b22a6 Add native Vicinae caffeinate extension
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.
2026-09-20 16:16:57 +08:00

Caffeinate

Keep your computer awake from Vicinae. Start, stop, and time-limit sleep prevention without touching a 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 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. detectPlatform throws with a clear message instead of failing obscurely. powercfg support 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

S
Description
Keep your computer awake from Vicinae. One toggle, no terminal.
Readme MIT
127 KiB
Languages
TypeScript 97.9%
JavaScript 2.1%