# Mags Python SDK

Execute scripts on [Magpie's](https://mags.run) instant VM infrastructure from Python.

## Install

```bash
pip install magpie-mags
```

## Quick Start

```python
from mags import Mags

m = Mags(api_token="your-token")

# Run a script and wait for the result
result = m.run_and_wait("echo 'Hello from a VM!'")
print(result["status"])    # "completed"
print(result["exit_code"]) # 0
for log in result["logs"]:
    print(log["message"])
```

## Authentication

Pass `api_token` directly, or set one of these environment variables:

```bash
export MAGS_API_TOKEN="your-token"
# or
export MAGS_TOKEN="your-token"
```

## Usage

### Run a Script

```python
# Fire-and-forget
job = m.run("apt install -y ffmpeg && ffmpeg -version")
print(job["request_id"])

# Run and wait for completion
result = m.run_and_wait(
    "python3 -c 'print(sum(range(100)))'",
    timeout=30,
)
```

### Persistent Workspaces

```python
# First run: creates the workspace
m.run_and_wait(
    "pip install pandas && echo 'setup done'",
    workspace_id="my-project",
    persistent=True,
)

# Second run: reuses the workspace (pandas is already installed)
m.run_and_wait(
    "python3 -c 'import pandas; print(pandas.__version__)'",
    workspace_id="my-project",
)
```

### Always-On VMs

```python
# VM that never auto-sleeps — stays running 24/7
job = m.run(
    "python3 server.py",
    workspace_id="my-api",
    persistent=True,
    no_sleep=True,
)
# Auto-recovers if the host goes down
```

### Enable URL / SSH Access

```python
job = m.run("python3 -m http.server 8080", persistent=True)

# HTTP access
access = m.enable_access(job["request_id"], port=8080)
print(access["url"])

# SSH access
ssh = m.enable_access(job["request_id"], port=22)
print(f"ssh root@{ssh['ssh_host']} -p {ssh['ssh_port']}")
```

### Upload Files

```python
file_ids = m.upload_files(["data.csv", "config.json"])
result = m.run_and_wait(
    "ls /uploads && wc -l /uploads/data.csv",
    file_ids=file_ids,
)
```

### Cron Jobs

```python
cron = m.cron_create(
    name="nightly-backup",
    cron_expression="0 0 * * *",
    script="tar czf /workspace/backup.tar.gz /data",
    workspace_id="backups",
)

jobs = m.cron_list()
m.cron_update(cron["id"], enabled=False)
m.cron_delete(cron["id"])
```

### Check Usage

```python
usage = m.usage(window_days=7)
print(f"Jobs: {usage['total_jobs']}, VM seconds: {usage['vm_seconds']:.0f}")
```

## API Reference

| Method | Description |
|--------|-------------|
| `run(script, **opts)` | Submit a job (`persistent`, `no_sleep`, `workspace_id`, ...) |
| `run_and_wait(script, **opts)` | Submit and block until done |
| `new(name, **opts)` | Create a VM sandbox (`persistent=True` for S3) |
| `find_job(name_or_id)` | Find a running/sleeping job by name or workspace |
| `exec(name_or_id, command)` | Run a command on an existing VM via SSH |
| `stop(name_or_id)` | Stop a running job |
| `url(name_or_id, port)` | Enable public URL and return full URL |
| `resize(workspace, disk_gb)` | Resize a workspace's disk |
| `status(request_id)` | Get job status |
| `logs(request_id)` | Get job logs |
| `list_jobs(page, page_size)` | List recent jobs |
| `update_job(request_id, **opts)` | Update job config (`startup_command`, `no_sleep`) |
| `enable_access(request_id, port)` | Enable URL or SSH access |
| `usage(window_days)` | Get usage summary |
| `upload_file(path)` | Upload a file, returns file ID |
| `upload_files(paths)` | Upload multiple files |
| `url_alias_create(sub, ws_id)` | Create a stable URL alias |
| `url_alias_list()` | List URL aliases |
| `url_alias_delete(sub)` | Delete a URL alias |
| `cron_create(**opts)` | Create a cron job |
| `cron_list()` | List cron jobs |
| `cron_get(id)` | Get a cron job |
| `cron_update(id, **updates)` | Update a cron job |
| `cron_delete(id)` | Delete a cron job |

## Links

- Website: [mags.run](https://mags.run)
- Node.js SDK: `npm install @magpiecloud/mags`
- CLI: `npm install -g @magpiecloud/mags`
