# InstaVM Python SDK

Official Python client for [InstaVM](https://instavm.io) — secure code execution in ephemeral microVMs with browser automation, networking controls, and platform APIs.

## Installation

```bash
pip install instavm
```

**Requirements:** Python 3.8+

## Quick Start

```python
from instavm import InstaVM

client = InstaVM(api_key="your_api_key")

try:
    result = client.execute("print('hello from instavm')")
    print(result.get("stdout", ""))
finally:
    client.close_session()
```

## Table of Contents

- [Quick Start](#quick-start)
- [Code Execution](#code-execution)
  - [Sessions & Configuration](#sessions--configuration)
  - [File Operations](#file-operations)
  - [Async Execution](#async-execution)
- [Sessions & Sandboxes](#sessions--sandboxes)
- [VMs & Snapshots](#vms--snapshots)
- [Volumes](#volumes)
  - [Volume CRUD & Files](#volume-crud--files)
  - [VM Volume Attachments](#vm-volume-attachments)
- [Networking](#networking)
  - [Egress, Shares, SSH](#egress-shares-ssh)
- [Browser Automation](#browser-automation)
  - [Basic Browser Flow](#basic-browser-flow)
  - [Content Extraction](#content-extraction)
- [Computer Use](#computer-use)
- [Platform APIs](#platform-apis)
- [Error Handling](#error-handling)
- [Development & Testing](#development--testing)
- [Further Reading](#further-reading)
- [Changelog](#changelog)

---

## Code Execution

### Sessions & Configuration

```python
from instavm import InstaVM

client = InstaVM(
    api_key="your_api_key",
    cpu_count=2,
    memory_mb=1024,
    env={"APP_ENV": "dev"},
    metadata={"team": "platform"},
)

result = client.execute("print('session id:', 'ok')")
print(result)
print(client.session_id)
```

### File Operations

```python
client = InstaVM(api_key="your_api_key")

client.upload_file("local_script.py", "/app/local_script.py")
client.execute("python /app/local_script.py", language="bash")
client.download_file("output.json", local_path="./output.json")
```

### Async Execution

```python
client = InstaVM(api_key="your_api_key")

task = client.execute_async("sleep 5 && echo 'done'", language="bash")
result = client.get_task_result(task["task_id"], poll_interval=2, timeout=60)
print(result)
```

---

## Sessions & Sandboxes

```python
client = InstaVM(api_key="your_api_key")

# Get the publicly-reachable app URL (optionally for a specific port)
app_url = client.get_session_app_url(port=8080)
print(app_url.get("app_url"))

# List sandbox records with optional metadata filter and limit
sandboxes = client.list_sandboxes(metadata={"env": "production"}, limit=50)
print(len(sandboxes))
```

---

## VMs & Snapshots

```python
client = InstaVM(api_key="your_api_key")

# Create a basic VM
vm = client.vms.create(wait=True, metadata={"purpose": "dev"})

# Create a VM with pre-attached volumes
vm_with_vols = client.vms.create(
    wait=True,
    volumes=[{"volume_id": "vol_abc", "mount_path": "/data", "mode": "rw"}],
)

# List VMs
vms = client.vms.list()                 # GET /v1/vms  (running)
all_records = client.vms.list_all_records()  # GET /v1/vms/ (all records)

# Snapshot a running VM
snap_from_vm = client.vms.snapshot(vm_id=vm["vm_id"], wait=True, name="dev-base")

# Build a snapshot from an OCI image
snap_from_oci = client.snapshots.create(
    oci_image="docker.io/library/python:3.11-slim",
    name="python-3-11-dev",
    vcpu_count=2,
    memory_mb=1024,
    snapshot_type="user",
    build_args={
        "git_clone_url": "https://github.com/example/repo.git",
        "git_clone_branch": "main",
        "envs": {"PIP_INDEX_URL": "https://pypi.org/simple"},
    },
)

user_snaps = client.snapshots.list(snapshot_type="user")
```

---

## Volumes

### Volume CRUD & Files

```python
client = InstaVM(api_key="your_api_key")

# Create
volume = client.volumes.create(name="project-data", quota_bytes=10 * 1024 * 1024 * 1024)
volume_id = volume["id"]

# Read / Update
client.volumes.list(refresh_usage=True)
client.volumes.get(volume_id, refresh_usage=True)
client.volumes.update(volume_id, name="project-data-v2", quota_bytes=20 * 1024 * 1024 * 1024)

# File operations
client.volumes.upload_file(volume_id, file_path="./README.md", path="docs/README.md", overwrite=True)
files = client.volumes.list_files(volume_id, prefix="docs/", recursive=True, limit=1000)
download = client.volumes.download_file(volume_id, path="docs/README.md")
client.volumes.delete_file(volume_id, path="docs/README.md")

# Checkpoints
checkpoint = client.volumes.create_checkpoint(volume_id, name="pre-release")
client.volumes.list_checkpoints(volume_id)
client.volumes.delete_checkpoint(volume_id, checkpoint["id"])

# Cleanup
client.volumes.delete(volume_id)
```

### VM Volume Attachments

```python
vm = client.vms.create(wait=True)
vm_id = vm["vm_id"]

client.vms.mount_volume(vm_id, volume_id, mount_path="/data", mode="rw", wait=True)
client.vms.list_volumes(vm_id)
client.vms.unmount_volume(vm_id, volume_id, mount_path="/data", wait=True)
```

---

## Networking

### Egress, Shares, SSH

```python
client = InstaVM(api_key="your_api_key")

# Egress policy
policy = client.set_session_egress(
    allow_package_managers=True,
    allow_http=False,
    allow_https=True,
    allowed_domains=["pypi.org", "files.pythonhosted.org"],
)

# Public/private share links
share = client.shares.create(port=3000, is_public=False)
client.shares.update(share_id=share["share_id"], is_public=True)

# SSH key registration
key = client.add_ssh_key("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host")
```

---

## Browser Automation

### Basic Browser Flow

```python
client = InstaVM(api_key="your_api_key")

session = client.browser.create_session(viewport_width=1366, viewport_height=768)
session.navigate("https://example.com")
links = session.extract_elements("a", ["text", "href"])
shot_b64 = session.screenshot(full_page=True)
session.close()
```

### Content Extraction

LLM-friendly extraction with optional interactive-element and anchor discovery:

```python
client = InstaVM(api_key="your_api_key")

content = client.browser.extract_content(
    url="https://example.com/docs",
    include_interactive=True,
    include_anchors=True,
    max_anchors=30,
)

print(content["readable_content"].get("title"))
for anchor in (content.get("content_anchors") or [])[:5]:
    print(anchor.get("text"), anchor.get("selector"))
```

---

## Computer Use

Control a full desktop environment inside a VM session:

```python
client = InstaVM(api_key="your_api_key")

session_id = client.session_id

# Viewer URL and state
viewer = client.computer_use.viewer_url(session_id)
state = client.computer_use.get(session_id, "/state")

# Proxy methods (GET, POST, HEAD)
head_resp = client.computer_use.head(session_id, "/state")

# VNC websockify URL for remote desktop streaming
vnc = client.computer_use.vnc_websockify(session_id)
```

---

## Platform APIs

API keys, audit logs, and webhooks:

```python
client = InstaVM(api_key="your_api_key")

# API Keys
api_key = client.api_keys.create(description="ci key")

# Audit log
audit_page = client.audit.events(limit=25, status="success")

# Webhooks
endpoint = client.webhooks.create_endpoint(
    url="https://example.com/instavm/webhook",
    event_patterns=["vm.*", "snapshot.*"],
)

deliveries = client.webhooks.list_deliveries(limit=10)
```

---

## Error Handling

All SDK errors extend a typed hierarchy for precise `except` handling:

```python
from instavm import (
    InstaVM,
    AuthenticationError,
    ExecutionError,
    NetworkError,
    RateLimitError,
    SessionError,
)

client = InstaVM(api_key="your_api_key")

try:
    client.execute("raise Exception('boom')")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError:
    print("Rate limited")
except SessionError as exc:
    print(f"Session issue: {exc}")
except ExecutionError as exc:
    print(f"Execution failed: {exc}")
except NetworkError as exc:
    print(f"Network issue: {exc}")
```

---

## Development & Testing

```bash
pip install -e .                              # Install for development
python3 -m pytest tests/test_api_client.py -v # Unit tests
```

---

## Further Reading

- [Python SDK Overview](https://instavm.io/docs/sdks/python/overview)
- [VM Management](https://instavm.io/docs/sdks/python/vm-management)
- [Snapshots](https://instavm.io/docs/sdks/python/snapshots)
- [Egress and Networking](https://instavm.io/docs/sdks/python/egress-and-networking)
- [Platform APIs](https://instavm.io/docs/sdks/python/platform-apis)
- [Browser Automation](https://instavm.io/docs/sdks/python/browser-automation)
- [Error Handling](https://instavm.io/docs/sdks/python/error-handling)

---

## Changelog

Current package version: **0.13.0**

### 0.13.0

- [`get_session_app_url(session_id?, port?)`](#sessions--sandboxes) — session app URL with optional port
- [`list_sandboxes(metadata?, limit?)`](#sessions--sandboxes) — list sandbox records with metadata filtering
- [`computer_use.head(session_id, path)`](#computer-use) — HEAD proxy method for computer-use sessions
- [`computer_use.vnc_websockify(session_id)`](#computer-use) — VNC websockify URL for remote desktop streaming
- VM creation now accepts [`volumes`](#vms--snapshots) for pre-attached volume mounts

### 0.12.0

- Manager-based APIs across VMs, volumes, snapshots, shares, custom domains, computer use, API keys, audit, and webhooks
- Snapshot build args support for env vars and Git clone inputs
- Distinct VM list helpers for `/v1/vms` and `/v1/vms/`

For detailed history, see repository tags and PR history.
