ciderstack 0.1.1
CiderStack Fleet SDK for Python
Requires Python: >=3.8

# CiderStack Fleet SDK for Python

A Python client for managing macOS VMs across CiderStack Fleet nodes.

## Installation

```bash
pip install ciderstack
```

Or install from source:

```bash
cd sdk/python
pip install -e .
```

For pairing support (optional — not needed if using API tokens):

```bash
pip install -e ".[pairing]"
```

## Authentication

All API calls require authentication. Two methods are available:

### API Tokens (recommended)

The simplest way to authenticate. Generate a token from the CiderStack CLI or app, then pass it to the client:

```python
from ciderstack import FleetClient

client = FleetClient("192.168.1.100", api_token="csk_abc123...")
```

Tokens can be **read-only** (can only list/get data) or **full-access** (can also start/stop/delete VMs, etc.).

### Node ID (via pairing)

For advanced use cases, you can pair the SDK as a fleet node using a 6-digit code from the CiderStack UI:

```python
from ciderstack import FleetClient

# One-time pairing (requires `cryptography` package)
creds = FleetClient.pair("192.168.1.100", "123456", "my-ci-script")
print(f"Save this node ID: {creds['node_id']}")

# Use the node ID for all future connections
client = FleetClient("192.168.1.100", node_id=creds["node_id"])
```

## Quick Start

```python
from ciderstack import FleetClient

client = FleetClient("192.168.1.100", api_token="csk_abc123...")

# List VMs
vms = client.list_vms()
for vm in vms:
    print(f"{vm.name}: {vm.state}")

# Start a VM
client.start_vm("vm-uuid")

# Execute a command
result = client.exec_command(
    vm_id="vm-uuid",
    command="uname -a",
    ssh_user="admin",
    ssh_password="password"
)
print(result.stdout)
```

## Examples

### VM Management

```python
# Clone a VM
new_vm = client.clone_vm("vm-uuid", "my-clone")

# Update VM settings
client.update_vm_settings(
    vm_id="vm-uuid",
    cpu_count=8,
    memory_size=16384  # 16 GB
)

# Delete a VM
client.delete_vm("vm-uuid")
```

### Snapshots

```python
# Create a snapshot
snapshot = client.create_snapshot(
    vm_id="vm-uuid",
    name="pre-update",
    description="Before system update"
)

# List snapshots
snapshots = client.list_snapshots("vm-uuid")

# Restore snapshot
client.restore_snapshot("vm-uuid", snapshot.id)
```

### Fleet Overview

```python
# Get cluster-wide status
raw = client.get_fleet_overview()
stats = raw["overview"]["stats"]
print(f"Total nodes: {stats['totalNodes']}")
print(f"Running VMs: {stats['runningVMs']}")

# Get node stats
stats = client.get_node_stats()
print(f"CPU: {stats.cpu_usage_percent}%")
print(f"Memory: {stats.memory_used_gb}/{stats.memory_total_gb} GB")
```

### Image Management

```python
# Pull an OCI image
client.pull_oci_image("ghcr.io/myorg/macos-base:latest")

# Create a VM from image
vm_id = client.create_vm(
    name="ci-runner",
    cpu_count=4,
    memory_mb=8192,
    disk_gb=64,
    oci_image="ghcr.io/myorg/macos-base:latest"
)
```

## API Reference

### FleetClient

```python
FleetClient(host, node_id=None, api_token=None, port=9473, timeout=30)
```

| Parameter   | Type  | Default | Description                              |
|-------------|-------|---------|------------------------------------------|
| `host`      | `str` | —       | IP address or hostname                   |
| `node_id`   | `str` | `None`  | Trusted node ID (from pairing)           |
| `api_token` | `str` | `None`  | API token (from CiderStack CLI/UI)       |
| `port`      | `int` | `9473`  | Port number                              |
| `timeout`   | `int` | `30`    | Request timeout in seconds               |

Provide exactly one of `node_id` or `api_token`.

**Node Info**
- `get_node_info()` -> NodeInfo
- `get_node_stats()` -> NodeStats

**VM Management**
- `list_vms()` -> List[VM]
- `get_vm(vm_id)` -> VM | None
- `start_vm(vm_id)` -> bool
- `stop_vm(vm_id)` -> bool
- `start_vm_recovery(vm_id)` -> bool
- `clone_vm(vm_id, new_name)` -> VM
- `rename_vm(vm_id, new_name)` -> bool
- `delete_vm(vm_id)` -> bool
- `get_vm_settings(vm_id)` -> Dict
- `update_vm_settings(vm_id, ...)` -> bool

**Snapshots**
- `list_snapshots(vm_id)` -> List[Snapshot]
- `create_snapshot(vm_id, name, description)` -> Snapshot
- `restore_snapshot(vm_id, snapshot_id)` -> bool
- `delete_snapshot(vm_id, snapshot_id)` -> bool

**Command Execution**
- `exec_command(vm_id, command, ssh_user, ssh_password, timeout=300)` -> ExecResult (timeout in seconds; default 300 for long-running steps like artifact download)

**Tasks**
- `get_tasks(include_completed)` -> List[Task]

**Images**
- `push_image(vm_id, image_name, insecure)` -> str
- `list_ipsws()` -> List[Dict]
- `list_oci_images()` -> List[Dict]
- `download_ipsw(url, name, version)` -> bool
- `pull_oci_image(image_reference, username, password)` -> bool
- `create_vm(name, cpu_count, memory_mb, disk_gb, ipsw_path, oci_image)` -> str

**Fleet**
- `get_fleet_overview()` -> Dict
- `get_fleet_events(limit, event_type)` -> List[Dict]
- `get_remote_resources()` -> Dict

**Pairing** (static method, requires `cryptography`)
- `FleetClient.pair(host, code, name, port)` -> Dict

## License

MIT