trayfeel
by 00011110
README.md
# TrayFeel
**Plug-and-play CD/DVD tray emotive actuator for AI agents.**
TrayFeel turns a Linux optical drive into a physical body-language channel: open, close, pulse, wiggle, and a catalog of named emotions — exposed as a Python library, CLI, MCP server, and optional HTTP daemon.
Linux-first. MIT licensed. Pure-Python ioctl path (no `eject` binary required).
## Requirements
- Python 3.10+
- Linux with `/dev/sr*` optical drive supporting `OPEN_TRAY` / `CLOSE_TRAY`
- User membership in the `cdrom` group (typical on Ubuntu/Debian)
```bash
sudo usermod -aG cdrom "$USER"
# log out and back in, then:
groups # should list cdrom
```
## Install
```bash
pip install trayfeel
# or from a checkout:
pip install -e ".[dev]"
```
Optional env (see `.env.example`):
| Variable | Purpose |
|----------|---------|
| `TRAYFEEL_DEVICE` | Force device path (default: first `/dev/sr*`) |
| `TRAYFEEL_TOKEN` | Bearer token for HTTP daemon |
| `TRAYFEEL_CAMERA` | Optional `/dev/videoN` for vision feedback |
| `TRAYFEEL_HOST` / `TRAYFEEL_PORT` | Daemon bind (default `127.0.0.1:8765`) |
## Quick start
```bash
trayfeel list-drives
trayfeel doctor
trayfeel status
trayfeel pulse
trayfeel emote laugh
trayfeel emote nod_yes --dry-run
trayfeel catalog
```
Python:
```python
from trayfeel import Tray
with Tray() as tray:
print(tray.status())
tray.run("pulse")
tray.emote("wave")
```
## Safety
TrayFeel refuses reckless motion by default:
- **Exclusive flock** per device (`~/.cache/trayfeel/…`)
- **Rate limit** (default 30 actions/minute)
- **Minimum interval** between motions (default 200 ms)
- **Disc guard** — slam patterns (`pulse`, `wiggle`, `nod`, …) refused while a disc is present unless `force=True`
- **Cooldown** after many open/close cycles
Override with `--force` / `force=True` when you intentionally accept the risk.
## Emotions
Named choreographies in `trayfeel/emotions/catalog.yaml`:
`laugh`, `giggle`, `gasp`, `surprise`, `shrug`, `think`, `ponder`, `nod_yes`, `shake_no`, `applause`, `sulk`, `pout`, `excited`, `bored`, `angry`, `curious`, `wave`, `heartbeat`, `sigh`, `celebrate`, `confused`, `acknowledge`, `reject`, `greet`, `farewell`, `error`, `success`, `idle_breath`
```bash
trayfeel emote celebrate
trayfeel emote think --dry-run
```
## Primitives
`open`, `close`, `toggle`, `lock`, `unlock`, `status`, `pulse`, `wiggle`, `hold_open`, `nod`, `double_tap`, `spin_up`, `spin_down`, `sleep`, `wait`
## MCP server
```bash
trayfeel mcp
# or: python -m trayfeel mcp
```
Tools: `tray_status`, `tray_list_drives`, `tray_primitive`, `tray_emote`, `tray_catalog`, `tray_doctor`.
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"trayfeel": {
"command": "trayfeel",
"args": ["mcp"]
}
}
}
```
### Cursor
Add an MCP server entry pointing at the same command (`trayfeel mcp` or `python -m trayfeel mcp`). Ensure the environment has `cdrom` group permissions.
Tool schemas for OpenAI / Claude function calling live in `examples/openai_tools.json` and `examples/claude_tools.json`. Agent skill notes: `examples/agent_skill.md`.
## HTTP daemon
For LAN agents that cannot attach stdio MCP:
```bash
export TRAYFEEL_TOKEN="$(openssl rand -hex 16)"
trayfeel daemon --host 0.0.0.0 --port 8765
```
| Method | Path | Body |
|--------|------|------|
| GET | `/health` | — |
| GET | `/status` | — |
| GET | `/catalog` | — |
| POST | `/primitive` | `{"name":"pulse","open_ms":300}` |
| POST | `/emote` | `{"name":"wave"}` |
Send `Authorization: Bearer $TRAYFEEL_TOKEN` when a token is configured.
## Vision (optional)
If `TRAYFEEL_CAMERA=/dev/video0` is set and `ffmpeg` is installed, `trayfeel vision` captures a frame via V4L2 and returns a best-effort brightness/edge heuristic. Missing camera or ffmpeg returns `ok: false` with a reason — it never crashes the process.
## Library layout
```
trayfeel/
core.py # Tray, Emoter
primitives.py # motion primitives
safety.py # flock, rate limits, disc guards
hardware/linux.py
emotions/ # catalog.yaml + engine
mcp_server.py
daemon.py
cli.py
```
## License
MIT — Copyright (c) 2026 TrayFeel contributors
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing