Metadata-Version: 2.4
Name: mikesplore-vela
Version: 1.0.0
Summary: Vela — Linux remote-PC agent with LLM assistant
License: MIT
Project-URL: Homepage, https://github.com/mikesplore/vela
Project-URL: Repository, https://github.com/mikesplore/vela
Project-URL: Documentation, https://github.com/mikesplore/vela
Keywords: remote-control,agent,pc-control,linux,assistant
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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: Operating System :: POSIX :: Linux
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.110.0
Requires-Dist: uvicorn[standard]>=0.23.0
Requires-Dist: python-jose[cryptography]>=3.4.0
Requires-Dist: bcrypt>=4.0.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: psutil>=5.9.0
Requires-Dist: Pillow>=10.0.0
Requires-Dist: pyperclip>=1.8.0
Requires-Dist: pydantic>=2.7.0
Requires-Dist: pydantic-settings>=1.0.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: python-multipart>=0.0.6
Requires-Dist: APScheduler>=3.11.0
Requires-Dist: SQLAlchemy>=2.0.0
Requires-Dist: slowapi>=0.1.9
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: websockets
Requires-Dist: requests
Requires-Dist: starlette
Requires-Dist: resend
Requires-Dist: typing-extensions
Requires-Dist: spotipy>=2.24.0
Requires-Dist: speedtest-cli>=2.1.3
Requires-Dist: notify2

# Vela RemotePC Agent

**Control your Linux desktop from anywhere: via chat, API, or WebSocket tunnel.**

Vela is a FastAPI-based remote PC agent for Linux. It exposes your desktop's capabilities (filesystem, audio, display, processes, notifications, power management, etc.) through a secure REST API, optionally tunneled through a WebSocket relay for remote access.

**New here?** Read **[How it works](doc/HOW_IT_WORKS.md)** first for the mental model (API vs agent, tunnel, auth, config). Then come back here for setup.

## Features

- **Full system control API**: filesystem, audio, display, power, notifications, network, input control, system info, monitoring, processes, security, scheduler, maintenance, media, clipboard, Spotify, alerts, docker, push
- **LLM-powered assistant**: natural language chat interface via Fireworks AI with tool-calling
- **WebSocket tunnel**: connect to a remote VPS relay to access your PC from anywhere
- **Agent onboarding**: browser-based QR pairing with code/PIN fallback
- **JWT authentication**: bcrypt-hashed passwords, bearer token auth, rate-limited
- **Filesystem access control**: whitelist-based directory permissions
- **Rate limiting**: per-endpoint rate limits (default 150/min, auth endpoints 10/min)
- **systemd integration**: runs as user services with auto-restart

## How it works (summary)

Vela runs as **two processes**:

| Process | Role |
|---------|------|
| **Vela API** (`vela.service`) | Local FastAPI server that executes desktop operations |
| **Vela Agent** (`vela-agent.service`) | Outbound WebSocket tunnel to your VPS; forwards phone requests to the API |

Your phone -> VPS relay -> agent tunnel -> `127.0.0.1:8765` -> Linux.

The optional assistant uses the same API: an LLM picks tools, Vela runs them, the LLM summarizes.

**Full explanation** (flows, auth layers, pairing, config paths, code map): **[doc/HOW_IT_WORKS.md](doc/HOW_IT_WORKS.md)**

## Quick Start

### Prerequisites

#### System Requirements

- **Python 3.13+**
- **Linux desktop** (X11 or Wayland)

#### Required System Packages

Most of these are typically pre-installed on a modern Linux desktop. Missing tools affect only the corresponding feature.

| Feature | Required Commands | Install (Debian/Ubuntu) | Install (Fedora) | Install (Arch) |
|---------|------------------|------------------------|------------------|----------------|
| **Filesystem** | `xdg-open` | `xdg-utils` | `xdg-utils` | `xdg-utils` |
| **Audio** | `amixer`, `pactl`, `canberra-gtk-play` | `alsa-utils`, `pulseaudio-utils`, `libcanberra-gtk-module` | `alsa-utils`, `pulseaudio-utils`, `libcanberra-gtk3` | `alsa-utils`, `pulseaudio-utils`, `libcanberra` |
| **Display / Screenshot** | `xrandr`, `flameshot`, `xset`, `ffmpeg`, `busctl`, `brightnessctl`, `gsettings`, `loginctl`, `swaymsg` | `x11-xserver-utils`, `flameshot`, `x11-xserver-utils`, `ffmpeg`, `libglib2.0-bin`, `brightnessctl`, `systemd` | `xorg-xrandr`, `flameshot`, `xorg-xset`, `ffmpeg`, `glib2`, `brightnessctl`, `systemd` | `xorg-xrandr`, `flameshot`, `xorg-xset`, `ffmpeg`, `glib2`, `brightnessctl`, `systemd` |
| **Input Control** | `xdotool`, `xprop`, `xwininfo` | `xdotool`, `x11-utils` | `xdotool`, `xorg-xprop`, `xorg-xwininfo` | `xdotool`, `xorg-xprop`, `xorg-xwininfo` |
| **Media** | `playerctl` | `playerctl` | `playerctl` | `playerctl` |
| **Network** | `nmcli`, `bluetoothctl`, `rfkill`, `ping`, `speedtest-cli` | `network-manager`, `bluez`, `util-linux`, `iputils-ping`, `speedtest-cli` | `NetworkManager`, `bluez`, `util-linux`, `iputils`, `speedtest-cli` | `networkmanager`, `bluez`, `util-linux`, `iputils`, `speedtest-cli` |
| **Notifications** | `notify-send`, `dunstctl` | `libnotify-bin`, `dunst` | `libnotify`, `dunst` | `libnotify`, `dunst` |
| **Power** | `systemctl`, `powerprofilesctl` | `systemd`, `power-profiles-daemon` | `systemd`, `power-profiles-daemon` | `systemd`, `power-profiles-daemon` |
| **Processes** | `xdotool`, `xprop`, `xwininfo` | `xdotool`, `x11-utils` | `xdotool`, `xorg-xprop`, `xorg-xwininfo` | `xdotool`, `xorg-xprop`, `xorg-xwininfo` |
| **Security** | `loginctl`, `modprobe`, `pactl`, `pkill`, `last`, `who`, `ffmpeg` | `systemd`, `kmod`, `pulseaudio-utils`, `procps`, `util-linux`, `coreutils`, `ffmpeg` | `systemd`, `kmod`, `pulseaudio-utils`, `procps-ng`, `util-linux`, `coreutils`, `ffmpeg` | `systemd`, `kmod`, `pulseaudio-utils`, `procps-ng`, `util-linux`, `coreutils`, `ffmpeg` |
| **System Info** | `lspci`, `lsusb`, `dmidecode`, `nvidia-smi`, `xrandr` | `pciutils`, `usbutils`, `dmidecode`, `nvidia-smi`, `x11-xserver-utils` | `pciutils`, `usbutils`, `dmidecode`, `nvidia-smi`, `xorg-xrandr` | `pciutils`, `usbutils`, `dmidecode`, `nvidia-smi`, `xorg-xrandr` |
| **Maintenance** | `journalctl`, `systemctl`, `timedatectl`, `apt-get`/`dnf`/`pacman` | `systemd` | `systemd` | `systemd` |
| **Monitoring** | `nvidia-smi` | `nvidia-smi` (NVIDIA GPU only) | `nvidia-smi` (NVIDIA GPU only) | `nvidia-smi` (NVIDIA GPU only) |

> **Tip:** Run `which <command>` to check if a particular tool is already installed.
> Missing tools won't crash the app; the corresponding endpoint will return an appropriate error.

#### Optional Dependencies

- Fireworks AI API key (for the LLM-powered assistant at `/assistant/chat`)
- Resend API key (for email alerts: CPU/memory spike alerts and daily summaries)
- Spotify Developer credentials (for Spotify playback control)

### Setup

From source:

```bash
git clone https://github.com/mikesplore/vela.git
cd vela

# Run the setup script: it will ask you a few questions
# and generate everything automatically
./setup.sh
```

If Vela is installed globally (for example via pip), run:

```bash
pip install mikesplore-vela
vela --setup
```

This will:
- Wipe cached local auth tokens and relay credentials (fresh start)
- Prompt for credentials, VPS URL, and agent label
- Generate a `config.yaml` and fresh `.env` (empty relay secrets until pairing)
- Verify the VPS relay is reachable
- Force pairing, then restart `vela.service` and `vela-agent.service` so only the new credentials are used

> **VPS relay:** You can use the free relay at `vela.mikesplore.tech` or specify your own.
>
> `./setup.sh` also creates a virtual environment and installs Vela from the current source tree. A globally installed `vela --setup` only runs setup.
>
> Setup links `vela` and `vela-agent` into `~/.local/bin` so the commands work outside the virtualenv (as long as that directory is on your `PATH`).

### Post-Setup

Optional integrations (Fireworks, Resend, Spotify) can be filled during `vela --setup`.
If you skipped them, edit the live credentials file afterward:

```bash
vela --env
```

At minimum, set your **Fireworks AI API key** if you want the assistant:

```
FIREWORKS_API_KEY='your-actual-key-here'
```

If you want email alerts, also set:

```
RESEND_API_KEY='your-resend-key'
RECIPIENT_EMAIL='your-email@example.com'
```

Then manage services with:

```bash
vela --enable
```

OpenAPI docs available at `http://127.0.0.1:8765/docs`.

Operations dashboard (request audit, latency median/p95, recent errors) is at
`http://127.0.0.1:8765/admin/dashboard`: sign in with the same username/password as the API.

> **p95** means the 95th percentile latency: 95% of requests finished at or under that time. It is better than average for spotting slow outliers without being dominated by a single worst request.

Common commands:

```bash
vela --setup
vela --start
vela --stop
vela --restart
vela --status
vela --logs
vela --dashboard
vela --env
vela --mcp
vela-agent --start
vela-agent --stop
```

> `vela --setup` is the only onboarding path. It always starts fresh (no credential reuse).
> `vela --env` opens the `.env` file your services actually load (may differ from the repo copy).
> `vela --mcp` prints the MCP connection link and relay secret for use with Claude/Cline.
> After editing credentials, run `vela --restart` so both services reload the new values.

## Agent Registration

Vela uses pairing-based onboarding (`2026-07-pairing-v1`). The agent always initiates outbound requests; VPS never initiates to the device.

### 1) Start pairing session

Agent calls `POST /agents/register/start`:

```bash
curl -X POST http://<vps-url>:8000/agents/register/start \
  -H "Content-Type: application/json" \
  -d '{"agent_name":"my-agent","device_info":{"device_fingerprint":"host:user"}}'
```

VPS returns `agent_id`, `pairing_code`, optional `pairing_pin`, and `pairing_expires_in`.  
The agent creates the QR payload locally from the VPS URL, pairing code, and optional PIN, then shows it with a manual code/PIN fallback.

### 2) Android completes pair

Android app sends:

```bash
curl -X POST http://<vps-url>:8000/pair/complete \
  -H "Content-Type: application/json" \
  -d '{"pairing_code":"<code>","pairing_pin":"<pin>","agent_label":"My Phone"}'
```

Agent polls `GET /agents/register/status?agent_id=...` until it gets `PAIRED` + `activation_token`, then immediately calls activation. If activation reports an invalid token, the agent fetches status once and retries activation once with the returned token before it requests a new pairing session.

### 3) Agent activates

```bash
curl -X POST http://<vps-url>:8000/agents/register/activate \
  -H "Content-Type: application/json" \
  -d '{"agent_id":"<agent-id>","activation_token":"<one-time-token>"}'
```

VPS returns `relay_secret`, `credential`, and scopes.  
Agent persists:

- `RELAY_SECRET` (primary secret for VPS auth)
- `AGENT_CREDENTIAL` (returned credential)
- `AGENT_ID`

### 4) Runtime reconnect

On normal reconnect:

```bash
curl -X POST http://<vps-url>:8000/agents/<agent-id>/ws-token \
  -H "X-Secret: <relay-secret>"
```

Then agent connects `ws(s)://<vps>/tunnel?agent_id=<id>&token=<ws_token>`.

## Configuration

Vela uses two configuration sources:

- **`config.yaml`**: server settings (host, port, secret key, feature flags, etc.)
- **`.env`**: agent credentials/secrets (`RELAY_SECRET`, `AGENT_CREDENTIAL`, API keys, etc.)

### config.yaml

```yaml
host: 127.0.0.1
port: 8765
secret_key: <32+ character random string>
token_expire_minutes: 1440
username: admin
password_hash: <bcrypt hash>
log_level: INFO

allowed_origins: []
allowed_base_dirs:
  - /home/youruser

# Rate limiting
rate_limit_default: 100/minute
route_rate_limits:
  /auth/token: 10/minute
  /ping: 60/minute

# Feature flags
feature_flags:
  display: true
  audio: true
  power: true
  notifications: true
  network: true
  filesystem: true
  input_control: true
  system_info: true
  monitoring: true
  processes: true
  security: true
  scheduler: true
  maintenance: true
  media: true
  clipboard: true

# Fireworks AI assistant
fireworks_api_url: https://api.fireworks.ai/inference/v1
fireworks_api_key: <your-api-key>
fireworks_model: accounts/fireworks/models/deepseek-v4-flash
assistant_system_prompt: "You are Vela..."
assistant_action_pin: null
assistant_action_timeout_seconds: 120
assistant_enable_thinking: false
```

### Environment variables (.env)

See [.env.example](.env.example) for the full list. Key variables:

| Variable | Description |
|----------|-------------|
| `VPS_URL` | VPS relay URL (e.g. `http://your-vps:8000`) |
| `AGENT_ID` | VPS-issued agent identifier after pairing |
| `RELAY_SECRET` | Primary long-lived secret used as `X-Secret` for relay auth |
| `AGENT_CREDENTIAL` | Agent activation credential returned by `/agents/register/activate` |
| `AGENT_SECRET` | Backward-compatible alias (currently mirrors `RELAY_SECRET`) |
| `PUBLIC_ADDRESS` | Public address of this agent (optional, first registration) |
| `METADATA` | JSON metadata for agent registration (optional) |
| `FIREWORKS_API_KEY` | Fireworks AI API key for the LLM assistant |
| `RESEND_API_KEY` | Resend API key for email alerts |
| `RECIPIENT_EMAIL` | Email address for alert notifications |
| `SPOTIFY_CLIENT_ID` | Spotify Developer client ID |
| `SPOTIFY_CLIENT_SECRET` | Spotify Developer client secret |
| `MCP_SERVER_URL` | Public URL of the vela-mcp server (e.g. `https://mcp.mikesplore.tech`). Used by `vela --mcp` to build the ready-to-paste MCP link |

## API Endpoints

### Authentication

- `POST /auth/token`: Login, returns JWT bearer token
- `GET /auth/me`: Get current user

### System Routers

| Prefix | Description |
|--------|-------------|
| `/fs` | Read/write files, list directories, disk usage |
| `/audio` | Volume control, audio output switching |
| `/display` | Screen lock, display info, brightness, screenshots |
| `/power` | Shutdown, reboot, suspend, power profile |
| `/notifications` | Send desktop notifications |
| `/network` | Network info, WiFi management, bluetooth, ping |
| `/input` | Mouse/keyboard control |
| `/system` | OS, hardware, resource info |
| `/monitor` | CPU, memory, disk monitoring |
| `/processes` | List/kill processes, open/close apps |
| `/security` | Screen lock, webcam control, login history |
| `/scheduler` | Task scheduling |
| `/maintenance` | System maintenance tasks, services, timers |
| `/media` | Media playback control |
| `/clipboard` | Clipboard read/write |
| `/alerts` | Spike monitoring and email alerts |
| `/spotify` | Spotify playback and search |
| `/docker` | Docker daemon info, containers, logs, compose |
| `/push` | FCM device registration + send (via VPS relay) |
| `/capabilities` | Runtime capability probes and tool availability |
| `/admin` | Audit dashboard/events |

### Assistant

- `POST /assistant/chat`: Natural language chat with LLM-powered tool calling
- `POST /assistant/stream`: Streaming (SSE) version of the chat endpoint

### Health

- `GET /`: Root info (name, version, enabled modules)
- `GET /health`: Service health check
- `GET /ping`: Connectivity check

## Assistant (LLM Integration)

Vela includes a Fireworks AI-powered chat assistant at `/assistant/chat`. It uses a Fireworks-hosted model (default `deepseek-v4-flash`) with tool-calling to map natural language to system operations.

```bash
curl -X POST http://127.0.0.1:8765/assistant/chat \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"message": "How much storage do I have left?"}'
```

The assistant:
- Parses natural language into intent
- Selects the appropriate system tool (filesystem, processes, etc.)
- Returns a human-readable response with the result
- Supports streaming responses via `/assistant/stream`
- Requires PIN confirmation for destructive actions (configurable)

## Security

- **JWT authentication**: all routes require a valid bearer token (except `/auth/token`)
- **Rate limiting**: auth endpoints limited to 10 req/min, default 150/min
- **Filesystem whitelist**: restrict directory access
- **Destructive action confirmation**: file deletion, power operations require explicit action confirmation
- **Optional PIN gate**: high-risk AI actions can require a PIN

## Development

### Project Structure

```
vela/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI application entry point
│   ├── auth.py               # JWT authentication
│   ├── dependencies.py       # FastAPI dependencies
│   ├── middleware.py          # Request logging
│   ├── prompts.py            # Assistant system prompts
│   ├── rate_limiter.py        # Rate limiting setup
│   ├── agent/
│   │   ├── __init__.py
│   │   ├── agent.py          # Agent CLI entry point
│   │   ├── helpers.py        # Re-exports for compatibility
│   │   ├── local_auth.py     # Local API JWT for tunnel forwarding
│   │   ├── pairing.py        # Relay pairing / activation
│   │   ├── credentials.py    # Relay credential persistence
│   │   ├── loop.py           # Agent reconnect loop
│   │   ├── envutil.py        # Shared agent env helpers
│   │   └── tunnel.py         # WebSocket tunnel implementation
│   ├── setup/                # Fresh-start setup (wipe, write config, pair, restart)
│   ├── ui/                   # Browser pairing + setup wizard pages
│   ├── cli.py                # Service management CLI (vela --start/--restart/...)
│   ├── db/
│   │   ├── __init__.py
│   │   ├── models.py         # Database models
│   │   ├── audit_log.py      # Audit log storage
│   │   ├── capabilities.py   # Capability probe persistence
│   │   └── pending_actions.py # Pending action storage
│   ├── domain/               # Domain models (schemas)
│   │   ├── __init__.py
│   │   ├── assistant.py
│   │   ├── audio.py
│   │   ├── ...
│   │   └── system_info.py
│   ├── routers/              # System operation routers
│   │   ├── __init__.py
│   │   ├── registry.py       # Capability-gated router loading
│   │   ├── capabilities.py
│   │   ├── admin.py
│   │   ├── filesystem.py
│   │   ├── audio.py
│   │   ├── ...
│   │   └── system_info.py
│   ├── services/             # Business logic
│   │   ├── __init__.py
│   │   ├── filesystem.py
│   │   ├── audio.py
│   │   ├── ...
│   │   └── system_info.py
│   │   └── assistant/        # LLM assistant service
│   │       ├── __init__.py
│   │       ├── helpers.py    # Fireworks/LLM planning + streaming
│   │       ├── session.py    # Chat session store
│   │       ├── tool_exec.py  # Tool call execution against local API
│   │       ├── tools.py      # Tool registry definitions
│   │       ├── gatekeeper_tools.py # Gatekeeper tool definitions
│   │       ├── workflow.py   # Tool execution stages
│   │       ├── images.py     # Image payload handling
│   │       ├── safety.py
│   │       ├── stream.py
│   │       └── prompts.py
│   └── utils/
│       ├── __init__.py
│       ├── config.py         # Configuration loading (pydantic-settings)
│       ├── errors.py         # Error response models
│       ├── input_header.py   # Input header helper
│       ├── run_command.py    # Shell command execution
│       └── spotify_client.py # Spotify API client
├── tests/                    # Test suite
│   ├── routers/
│   ├── services/
│   └── db/
├── .env.example              # Environment variable template
├── config.yaml               # Local configuration (generated)
├── setup.sh                  # Setup script
├── pyproject.toml            # Python package metadata
└── README.md
```

### Adding a Route

1. Add a service file under `app/services/` for business logic (if needed)
2. Add a router file under `app/routers/` with endpoint definitions
3. Export the router in `app/routers/__init__.py`
4. Add `feature_flags` entry in `config.yaml` if needed
5. Add a domain model in `app/domain/` if new schemas are needed
6. Add tests under `tests/`

## Running Tests

```bash
python -m pytest
```

## License

[MIT](LICENSE)
