awake-axi
v0.1.1
Published
Battery-aware macOS sleep inhibition for long-running agent tasks
Readme
awake-axi
Your agent should not lose an hour of work because you walked away from your Mac.
Long Codex and Claude tasks often keep working after the human leaves. macOS sees an idle laptop and may put it to sleep; the build, browser flow, model run, or deployment stops with it.
Running caffeinate forever solves the interruption by creating a different
problem: an unplugged Mac can stay awake until its battery is empty.
awake-axi gives each agent task a battery-aware sleep-inhibition lease. It keeps the system awake while power is safe, releases its assertion when charge drops below the configured floor, and cleans up when the task ends.
long agent task starts
│
▼
AC power or battery ≥ 35%? ── no ──▶ normal macOS sleep policy
│ yes ▲
▼ │ rechecked every 30s
PreventUserIdleSystemSleep │
│
▼
task ends / stop / TTL expires ───────────┘Proof, not promise
The repository contains both deterministic integration tests and a captured
test against the real macOS power-management subsystem. It also contains two
black-box agent runs proving that Codex and Claude discover and invoke the
skill from a prompt that never names awake-axi.
On an arm64 Mac running on battery at 89%, awake-axi start reported:
sleep_prevented: true
monitor_pid: 56676
inhibitor_pid: 56678At the same moment, pmset -g assertions independently observed the OS-level
assertion owned by that exact process:
pid 56678(caffeinate): ... PreventUserIdleSystemSleep named: "caffeinate command-line tool"After awake-axi stop, the probe verified all three cleanup signals:
monitor_after_stop: absent
inhibitor_after_stop: absent
assertion_after_stop: absent
verdict: PASS- Full real-macOS smoke output
- Automated check: 8 tests passed
- Installed skill validation
- Codex + Claude implicit-invocation evidence
Reproduce the live assertion check on a Mac:
npm ci
npm run build
scripts/live-smoke.shThe smoke fails unless it sees the assertion for its own caffeinate PID and
then proves that the PID and assertion disappear after cleanup.
What it protects
| Failure mode | awake-axi control | Observable check |
|---|---|---|
| macOS sleeps during unattended work | caffeinate -i assertion scoped to a lease | pmset -g assertions names the lease's inhibitor PID |
| an unplugged task drains the battery | configurable battery floor, checked throughout the lease | integration test moves power 20% → 90% → 10% and observes off → on → off |
| a crashed or forgotten task holds the Mac forever | hard expiry, 8 hours by default | persisted expires_at; monitor exits and releases its child |
| one agent stops another agent's protection | unique session IDs and validated monitor PIDs | stop accepts one session ID and verifies process ownership before signalling |
| the detached monitor cannot start | fail closed and persist an error state | subprocess test supplies a missing runtime binary and verifies an error instead of a crash |
| the inhibitor fails to spawn while a lease is already running | monitor stays alive, persists the error, and retries on the next poll instead of crashing | integration test breaks the caffeinate path mid-lease and verifies the monitor process survives with lastError set |
| keeping the system awake weakens screen privacy | only idle system sleep is inhibited | no caffeinate -d; display sleep and screen locking remain available |
Quick start
Start a lease before a long task and save its session ID:
awake-axi start
# session_id: 875fcf0d2510
# sleep_prevented: trueInspect or release only that lease:
awake-axi status 875fcf0d2510
awake-axi stop 875fcf0d2510For one shell command, let the CLI own cleanup automatically:
awake-axi run -- make test
awake-axi run -- codex exec "finish the migration and run its tests"Tune the safety boundary when needed:
awake-axi start --min-battery 50 --max-hours 4 --poll-seconds 15Install the CLI and skill
Install the CLI from npm, then copy its bundled skill into the personal skill directories used by Codex and Claude Code:
npm install --global awake-axi
awake-axi skill installskill install copies the bundled skill to
~/.agents/skills/awake-axi for Codex and ~/.claude/skills/awake-axi for
Claude Code. Both agents can select it automatically from its description. The
skill teaches them when a task is long enough to justify a lease, requires them
to preserve the returned session ID, and makes explicit cleanup part of
finishing the task.
Safety boundaries
awake-axi is intentionally smaller than a general power-management app:
- AC power is always sufficient; battery power must be at or above
--min-battery(default 35%). - Power is checked every 30 seconds by default. The assertion is stopped below the floor and restored if power later becomes sufficient.
- Every lease expires after 8 hours by default. Its owner should still call
stopexplicitly. - Unknown power state fails safe and does not inhibit sleep.
- State contains no secrets and lives in
~/Library/Caches/awake-axi. - It prevents idle system sleep only. It does not keep the display on, defeat screen locking, or keep a MacBook awake after the lid is closed.
- It never uses sudo or changes persistent/global
pmsetsettings.
Why not just use something else?
- Use plain
caffeinate -i <command>when one foreground command is enough and battery-aware behavior is unnecessary. - Use Keepresso or Amphetamine when you need a menu-bar UI, closed-lid operation, schedules, triggers, or thermal controls.
- Use awake-axi when an agent needs a small, machine-readable, unprivileged lease that it can own and prove end to end.
The design choice and alternative analysis are documented in the spark research and SWOT.
Development
npm ci
npm run checkRelease
Production releases are published to npm by
.github/workflows/publish.yml when a non-prerelease GitHub Release is
published. The release tag must be v followed by the exact version in
package.json; for example, package version 0.2.0 requires tag v0.2.0.
Prepare the next version before creating the GitHub Release:
npm version patch --no-git-tag-version
npm run checkThe workflow uses npm Trusted Publishing with GitHub OIDC and emits provenance,
so it does not require a long-lived NPM_TOKEN secret.
npm run check builds the package, runs pure power-policy tests and subprocess
integration tests, validates the bundled skill, and checks the npm tarball.
