Metadata-Version: 2.5
Name: human-presence
Version: 0.1.0
Summary: Keep macOS awake with pointer motion that actually looks human.
Author-email: Benyamin <benyaminjmf@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Benyamin
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: automation,cursor,human-like,idle,keep-awake,macos,mouse,presence,quartz
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Desktop Environment
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: pyobjc-framework-quartz>=9.0; sys_platform == 'darwin'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

# human-presence

Keep macOS "in use" with pointer motion that actually looks human.

Most keep-awake tools jump the cursor one pixel and call it a day. That is a
teleport: no path, no acceleration, always the same direction. This one
generates real trajectories instead.

```bash
human-presence -g 25 90
```

```
--------------------------------------------------------------
human-presence   gap 25-90s   speed x1
  display 0: 1512x982 at (0,0)
  display 1: 1920x1080 at (1512,-459)
  Ctrl-C to stop.
--------------------------------------------------------------
10:54:52  micro    20px -> (1185,452)
10:55:33  hop      79px -> (1257,518)
10:55:33  scrolled 4 notches up
10:56:41  travel  612px -> (410,244)
```

## What makes the motion human

| Property | How it is modelled |
| --- | --- |
| Path shape | Cubic Bézier with a randomised bow. ~75% single arc, ~25% lazy S. Never a straight line. |
| Speed | Minimum-jerk profile — zero velocity at both ends, so it eases in and eases out. |
| Duration | Fitts's law, `MT = a + b·log2(D/W + 1)`, with per-move noise. 50 px ≈ 280 ms, 1200 px ≈ 730 ms. |
| Precision | Per-step tremor, plus overshoot and a corrective submovement on ~35% of long reaches. |
| Hesitation | Occasional mid-flight pause on long travels, like a person re-aiming. |
| Timing | Log-uniform gaps, so short pauses outnumber long ones. Sometimes a burst of 2–4 quick actions. |
| Target choice | ~45% micro-drift (2–25 px), ~35% short hop (60–350 px), ~20% long travel across the screen. |
| Scrolling | Bursts of 2–6 notches in one direction, each weaker than the last. |

## Install

Requires macOS and Python 3.9+.

```bash
pip install human-presence
```

From a checkout:

```bash
git clone https://github.com/agn-7/human-presence.git
cd human-presence
pip install -e '.[dev]'
```

### Accessibility permission

macOS will not deliver synthetic pointer events until you allow it. Open
**System Settings → Privacy & Security → Accessibility** and enable whichever
app runs the command — Terminal, iTerm, or your IDE.

Without permission the events are silently dropped. Use `--verify` to check: if
the idle clock does not reset to `0.0s` after an action, permission is missing.

## Usage

```bash
human-presence [-g MIN MAX] [--speed X] [--hours HH:MM-HH:MM] [--display N]
               [--margin PX] [--no-scroll] [--keys] [--yield-seconds S]
               [--dry-run] [--verify] [--seed N] [-q]
```

| Flag | Meaning |
| --- | --- |
| `-g`, `--gap MIN MAX` | Seconds of **real** inactivity before acting. A fresh random value in this range every time. Default `25 90`. |
| `--speed X` | Movement speed multiplier. `0.7` is slower and calmer. Default `1.0`. |
| `--hours HH:MM-HH:MM` | Only run inside this daily window. Overnight wrap works: `22:00-06:00`. |
| `--display N` | Pin to one display. Default: whichever screen holds the cursor. |
| `--margin PX` | Pixels to keep clear of every screen edge. Default `60`. |
| `--no-scroll` | Never scroll. |
| `--keys` | Also tap shift occasionally. Off by default. |
| `--yield-seconds S` | How long to stand down after you touch the mouse. Default `120`. |
| `--dry-run` | Print the plan, never touch the cursor. |
| `--verify` | Print the system idle clock before and after each action. |
| `--seed N` | Fix the RNG, for reproducible tests. |
| `-q`, `--quiet` | Only log actions. |

### Choosing a gap

Keep the maximum below your screen-sleep setting (System Settings → Lock Screen).

| Command | Behaviour |
| --- | --- |
| `-g 25 90` | Good default. |
| `-g 60 240` | Calmer. Use when screen sleep is 5 minutes or more. |
| `-g 10 30` | Busy. Only when something needs very frequent activity. |

### Preview before the real run

```bash
human-presence --dry-run -g 3 6
```

Nothing moves. You only see what it *would* do.

## How idle is detected

`CGEventSourceSecondsSinceLastEventType` — the same clock the screensaver reads.
Keyboard activity counts, not just the mouse. So the tool stays quiet while you
are genuinely working, and only acts once you actually stop.

Because that clock is also reset by the tool's own events, a gap is measured
from whichever came last: you, or it.

## Staying out of the way

- **You always win.** Touch the mouse and it stands down for `--yield-seconds`.
  It also aborts a move mid-flight if the cursor stops tracking the path.
- **No hot corners.** Every point is clamped into a safe box: inset from all
  edges, below the menu bar, clear of the corners that trigger Mission Control.
- **Multi-display aware.** Bounds come from `CGGetActiveDisplayList`, and the
  cursor position from `CGEventGetLocation`, which is correct in global
  coordinates across stacked or offset monitor layouts.

## Run it at login

The repository ships a ready template at `packaging/com.agn7.human-presence.plist`.
Copy it to `~/Library/LaunchAgents/`, edit the paths and flags inside, then:

```bash
launchctl load ~/Library/LaunchAgents/com.agn7.human-presence.plist
launchctl list | grep human-presence          # confirm it is running
launchctl unload ~/Library/LaunchAgents/com.agn7.human-presence.plist   # stop
```

Logs go to `/tmp/human-presence.log` and `/tmp/human-presence.err.log`.
launchd does not expand `~`, so use absolute paths if you change them.

Grant Accessibility permission to `/usr/local/bin/python3` (or whichever
interpreter the plist points at), not to Terminal.

## Development

```bash
pip install -e '.[dev]'
pytest
ruff check .
```

Tests cover the movement model, the safe-area maths, and the CLI. The parts
that need macOS are marked and skipped elsewhere; the geometry and timing tests
run anywhere.

## Prior art

The idea comes from [carrot69/keep-presence](https://github.com/carrot69/keep-presence)
(CC0). This is an independent implementation with a different movement model —
trajectories instead of a one-pixel teleport, an idle clock instead of position
comparison, and no `pynput` dependency.

## License

MIT. The full text is in the `LICENSE` file of the source distribution.
