# CLI- und SDK-Leitfaden

## Übersicht

Clore.ai bietet eine **REST-API** die vollständigen programmgesteuerten Zugriff auf den GPU-Marktplatz ermöglicht — Auflisten von Servern, Erstellen von Bestellungen, Überwachen von Deployments und Stornieren von Vermietungen.

> **Hinweis:** Es gibt derzeit kein offizielles CLI-Binary. Alle Automatisierungen erfolgen direkt über die REST-API mit Tools wie `curl`, Python oder Node.js.

**Basis-URL:** `https://api.clore.ai/v1`

**Antwortformat:** JSON. Jede Antwort enthält ein `code` Feld, das den Status angibt.

***

## Authentifizierung

Generieren Sie Ihren API-Schlüssel aus dem [Clore.ai Dashboard](https://clore.ai):

1. Melden Sie sich bei Ihrem Konto an
2. Navigieren Sie zu **API** Abschnitt in den Einstellungen
3. Generieren und kopieren Sie Ihren API-Schlüssel

**Header-Format:**

```
auth: IHRE_API_KEY
```

> ⚠️ **Wichtig:** Der auth-Header ist `auth`, **nicht** `Authorization: Bearer`. Die Verwendung des falschen Formats gibt den Code `3` (Ungültiges API-Token) zurück.

**Beispiel:**

```bash
curl -H 'auth: IHRE_API_KEY' 'https://api.clore.ai/v1/marketplace'
```

***

## Schnellstart

### Marktplatz-Server auflisten

Durchsuchen Sie alle verfügbaren GPU-Server:

```bash
curl -XGET \
  -H 'auth: IHRE_API_KEY' \
  'https://api.clore.ai/v1/marketplace'
```

**Antwort:**

```json
{
  "servers": [
    {
      "id": 6,
      "owner": 4,
      "mrl": 73,
      "price": {
        "on_demand": { "bitcoin": 0.00001 },
        "spot": { "bitcoin": 0.000001 }
      },
      "rented": false,
      "specs": {
        "cpu": "Intel Core i9-11900",
        "ram": 62.67,
        "gpu": "1x NVIDIA GeForce GTX 1080 Ti",
        "gpuram": 11,
        "net": { "up": 26.38, "down": 118.42, "cc": "CZ" }
      }
    }
  ],
  "my_servers": [1, 2, 4],
  "code": 0
}
```

***

### Ihre Bestellungen abrufen

```bash
curl -XGET \
  -H 'auth: IHRE_API_KEY' \
  'https://api.clore.ai/v1/my_orders'
```

Abgeschlossene/abgelaufene Bestellungen einbeziehen:

```bash
curl -XGET \
  -H 'auth: IHRE_API_KEY' \
  'https://api.clore.ai/v1/my_orders?return_completed=true'
```

***

### Eine Bestellung erstellen (On-Demand)

```bash
curl -XPOST \
  -H 'auth: IHRE_API_KEY' \
  -H 'Content-type: application/json' \
  -d '{
    "currency": "bitcoin",
    "image": "cloreai/ubuntu20.04-jupyter",
    "renting_server": 6,
    "type": "on-demand",
    "ports": {
      "22": "tcp",
      "8888": "http"
    },
    "ssh_password": "YourSSHPassword123",
    "jupyter_token": "YourJupyterToken123"
  }' \
  'https://api.clore.ai/v1/create_order'
```

**Antwort:**

```json
{ "code": 0 }
```

***

### Einen Spot-Order erstellen

Spot-Bestellungen sind günstiger, können aber überboten werden. Sie legen Ihren Preis pro Tag fest:

```bash
curl -XPOST \
  -H 'auth: IHRE_API_KEY' \
  -H 'Content-type: application/json' \
  -d '{
    "currency": "bitcoin",
    "image": "cloreai/ubuntu20.04-jupyter",
    "renting_server": 6,
    "type": "spot",
    "spotprice": 0.000005,
    "ports": {
      "22": "tcp",
      "8888": "http"
    },
    "ssh_password": "YourSSHPassword123"
  }' \
  'https://api.clore.ai/v1/create_order'
```

***

### Bestellstatus prüfen

```bash
curl -XGET \
  -H 'auth: IHRE_API_KEY' \
  'https://api.clore.ai/v1/my_orders'
```

Aktive Bestellungen enthalten `pub_cluster` (Hostnamen) und `tcp_ports` für SSH-Zugriff:

```json
{
  "id": 38,
  "pub_cluster": ["n1.c1.clorecloud.net", "n2.c1.clorecloud.net"],
  "tcp_ports": ["22:10000"],
  "http_port": "8888",
  "expired": false
}
```

SSH auf Ihren gemieteten Server:

```bash
ssh root@n1.c1.clorecloud.net -p 10000
```

***

### Eine Bestellung stornieren

```bash
curl -XPOST \
  -H 'auth: IHRE_API_KEY' \
  -H 'Content-type: application/json' \
  -d '{
    "id": 38
  }' \
  'https://api.clore.ai/v1/cancel_order'
```

Optional können Sie ein Problem mit dem Server melden:

```bash
curl -XPOST \
  -H 'auth: IHRE_API_KEY' \
  -H 'Content-type: application/json' \
  -d '{
    "id": 38,
    "issue": "GPU wurde überhitzt und drosselte die Leistung"
  }' \
  'https://api.clore.ai/v1/cancel_order'
```

***

## Python SDK

Ein leichter Wrapper, der die `requests` Bibliothek verwendet. Installieren Sie sie mit:

```bash
pip install requests
```

### CloreClient-Klasse

```python
import requests
import time

class CloreClient:
    """Ein einfaches Python-SDK für die Clore.ai REST-API."""
    
    BASE_URL = "https://api.clore.ai/v1"
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({"auth": api_key})
    
    def _get(self, endpoint: str, params: dict = None) -> dict:
        url = f"{self.BASE_URL}/{endpoint}"
        response = self.session.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        if data.get("code") != 0:
            raise CloreAPIError(data)
        return data
    
    def _post(self, endpoint: str, body: dict) -> dict:
        url = f"{self.BASE_URL}/{endpoint}"
        self.session.headers.update({"Content-type": "application/json"})
        response = self.session.post(url, json=body)
        response.raise_for_status()
        data = response.json()
        if data.get("code") != 0:
            raise CloreAPIError(data)
        return data
    
    def list_servers(self) -> list:
        """Alle verfügbaren Server auf dem Marktplatz abrufen."""
        data = self._get("marketplace")
        return data["servers"]
    
    def get_server_details(self, server_id: int) -> dict:
        """Spot-Marktplatzinformationen für einen bestimmten Server abrufen."""
        data = self._get("spot_marketplace", params={"market": server_id})
        return data["market"]
    
    def create_order(
        self,
        server_id: int,
        image: str,
        order_type: str = "on-demand",
        currency: str = "bitcoin",
        spotprice: float = None,
        ports: dict = None,
        ssh_password: str = None,
        ssh_key: str = None,
        jupyter_token: str = None,
        env: dict = None,
        command: str = None,
    ) -> dict:
        """
        Erstellt eine On-Demand- oder Spot-Bestellung.
        
        Parameter:
            server_id: ID des zu mietenden Servers
            image: Docker-Image (z. B. 'cloreai/ubuntu20.04-jupyter')
            order_type: 'on-demand' oder 'spot'
            currency: 'bitcoin' (Standard)
            spotprice: Erforderlich für Spot-Bestellungen — Preis pro Tag in BTC
            ports: Portweiterleitung, z. B. {"22": "tcp", "8888": "http"}
            ssh_password: SSH-Passwort (nur alphanumerische Zeichen)
            ssh_key: SSH-öffentlicher Schlüssel
            jupyter_token: Jupyter-Notebook-Token
            env: Dictionary mit Umgebungsvariablen
            command: Shell-Befehl, der nach dem Start des Containers ausgeführt wird
        """
        if order_type == "spot" and spotprice is None:
            raise ValueError("spotprice is required for spot orders")
        
        body = {
            "currency": currency,
            "image": image,
            "renting_server": server_id,
            "type": order_type,
        }
        
        if spotprice is not None:
            body["spotprice"] = spotprice
        if ports:
            body["ports"] = ports
        if ssh_password:
            body["ssh_password"] = ssh_password
        if ssh_key:
            body["ssh_key"] = ssh_key
        if jupyter_token:
            body["jupyter_token"] = jupyter_token
        if env:
            body["env"] = env
        if command:
            body["command"] = command
        
        return self._post("create_order", body)
    
    def get_orders(self, include_completed: bool = False) -> list:
        """Ihre aktiven (und optional abgeschlossenen) Bestellungen abrufen."""
        params = {"return_completed": "true"} if include_completed else {}
        data = self._get("my_orders", params=params)
        return data["orders"]
    
    def cancel_order(self, order_id: int, issue: str = None) -> dict:
        """Eine Bestellung stornieren. Optional ein Problem melden."""
        body = {"id": order_id}
        if issue:
            body["issue"] = issue
        return self._post("cancel_order", body)
    
    def get_wallets(self) -> list:
        """Ihre Wallets und Kontostände abrufen."""
        data = self._get("wallets")
        return data["wallets"]
    
    def get_my_servers(self) -> list:
        """Server abrufen, die Sie dem Marktplatz bereitstellen."""
        data = self._get("my_servers")
        return data["servers"]


class CloreAPIError(Exception):
    """Wird ausgelöst, wenn die Clore-API einen von null verschiedenen Code zurückgibt."""
    
    ERROR_CODES = {
        0: "Normal",
        1: "Datenbankfehler",
        2: "Ungültige Eingabedaten",
        3: "Ungültiges API-Token",
        4: "Ungültiger Endpunkt",
        5: "Rate-Limit überschritten (1 Anfrage/Sekunde)",
        6: "Fehler (siehe Fehlerfeld)",
    }
    
    def __init__(self, response: dict):
        self.code = response.get("code")
        self.error = response.get("error", "")
        message = self.ERROR_CODES.get(self.code, f"Unbekannter Code {self.code}")
        if self.error:
            message = f"{message}: {self.error}"
        super().__init__(f"Clore API Fehler {self.code}: {message}")
```

### Vollständiges funktionierendes Beispiel

```python
import os
from clore_client import CloreClient, CloreAPIError

# Client initialisieren
client = CloreClient(api_key=os.environ["CLORE_API_KEY"])

# 1. Marktplatz durchsuchen
servers = client.list_servers()
print(f"Gefunden {len(servers)} Server auf dem Marktplatz")

# 2. Verfügbare RTX 4090-Server filtern
rtx4090_servers = [
    s for s in servers
    if "4090" in s["specs"].get("gpu", "") and not s["rented"]
]

if not rtx4090_servers:
    print("Keine verfügbaren RTX 4090-Server gefunden")
    exit(1)

# 3. Den günstigsten auswählen
cheapest = min(rtx4090_servers, key=lambda s: s["price"]["on_demand"]["bitcoin"])
print(f"Günstigste RTX 4090: Server-ID {cheapest['id']}, "
      f"Preis {cheapest['price']['on_demand']['bitcoin']:.8f} BTC/Tag")

# 4. Eine Bestellung erstellen
try:
    client.create_order(
        server_id=cheapest["id"],
        image="cloreai/ubuntu20.04-jupyter",
        order_type="on-demand",
        currency="bitcoin",
        ports={"22": "tcp", "8888": "http"},
        ssh_password="SecurePass123",
        jupyter_token="MyToken123",
    )
    print("Bestellung erfolgreich erstellt!")
except CloreAPIError as e:
    print(f"Fehler beim Erstellen der Bestellung: {e}")
    exit(1)

# 5. Ihre Bestellungen prüfen
orders = client.get_orders()
for order in orders:
    if not order.get("expired"):
        cluster = order.get("pub_cluster", [])
        tcp = order.get("tcp_ports", [])
        print(f"Bestellung {order['id']}: Server {order['si']}")
        if cluster and tcp:
            ssh_port = tcp[0].split(":")[1]
            print(f"  SSH: ssh root@{cluster[0]} -p {ssh_port}")
```

***

## Node.js-Beispiel

Verwendung der nativen `fetch` API (Node.js 18+):

```javascript
const BASE_URL = 'https://api.clore.ai/v1';

class CloreClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.defaultHeaders = {
      'auth': apiKey,
    };
  }

  async get(endpoint, params = {}) {
    const url = new URL(`${BASE_URL}/${endpoint}`);
    Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
    
    const res = await fetch(url.toString(), {
      headers: this.defaultHeaders,
    });
    
    const data = await res.json();
    if (data.code !== 0) {
      throw new Error(`Clore API error ${data.code}: ${data.error || 'unknown'}`);
    }
    return data;
  }

  async post(endpoint, body) {
    const res = await fetch(`${BASE_URL}/${endpoint}`, {
      method: 'POST',
      headers: {
        ...this.defaultHeaders,
        'Content-type': 'application/json',
      },
      body: JSON.stringify(body),
    });
    
    const data = await res.json();
    if (data.code !== 0) {
      throw new Error(`Clore API error ${data.code}: ${data.error || 'unknown'}`);
    }
    return data;
  }

  async listServers() {
    const data = await this.get('marketplace');
    return data.servers;
  }

  async getOrders(includeCompleted = false) {
    const params = includeCompleted ? { return_completed: 'true' } : {};
    const data = await this.get('my_orders', params);
    return data.orders;
  }

  async createOrder({ serverId, image, type = 'on-demand', currency = 'bitcoin', spotprice, ports, sshPassword, jupyterToken, env, command }) {
    const body = {
      currency,
      image,
      renting_server: serverId,
      type,
      ...(spotprice && { spotprice }),
      ...(ports && { ports }),
      ...(sshPassword && { ssh_password: sshPassword }),
      ...(jupyterToken && { jupyter_token: jupyterToken }),
      ...(env && { env }),
      ...(command && { command }),
    };
    return this.post('create_order', body);
  }

  async cancelOrder(orderId, issue = null) {
    const body = { id: orderId };
    if (issue) body.issue = issue;
    return this.post('cancel_order', body);
  }
}

// Verwendungsbeispiel
const client = new CloreClient(process.env.CLORE_API_KEY);

async function main() {
  // Marktplatz auflisten
  const servers = await client.listServers();
  console.log(`Der Marktplatz hat ${servers.length} Server`);

  // Verfügbare RTX 4090-Server finden
  const available = servers.filter(
    s => s.specs.gpu.includes('4090') && !s.rented
  );
  console.log(`Verfügbare RTX 4090-Server: ${available.length}`);

  // Aktuelle Bestellungen abrufen
  const orders = await client.getOrders();
  const activeOrders = orders.filter(o => !o.expired);
  console.log(`Aktive Bestellungen: ${activeOrders.length}`);

  for (const order of activeOrders) {
    const host = order.pub_cluster?.[0];
    const portMapping = order.tcp_ports?.[0];
    if (host && portMapping) {
      const sshPort = portMapping.split(':')[1];
      console.log(`Bestellung ${order.id} → ssh root@${host} -p ${sshPort}`);
    }
  }
}

main().catch(console.error);
```

***

## Gängige Workflows

### Günstigste RTX 4090 finden und mieten

```python
from clore_client import CloreClient

client = CloreClient(api_key="YOUR_API_KEY")

def rent_cheapest_rtx4090(image="cloreai/ubuntu20.04-jupyter", ssh_password="SecurePass123"):
    servers = client.list_servers()
    
    # Filter: verfügbare RTX 4090
    candidates = [
        s for s in servers
        if "4090" in s["specs"].get("gpu", "")
        and not s["rented"]
    ]
    
    if not candidates:
        raise RuntimeError("Keine verfügbaren RTX 4090-Server gefunden")
    
    # Nach On-Demand-BTC-Preis sortieren
    candidates.sort(key=lambda s: s["price"]["on_demand"]["bitcoin"])
    best = candidates[0]
    
    price_btc = best["price"]["on_demand"]["bitcoin"]
    print(f"Miete Server {best['id']}: {best['specs']['gpu']} @ {price_btc:.8f} BTC/Tag")
    
    client.create_order(
        server_id=best["id"],
        image=image,
        order_type="on-demand",
        currency="bitcoin",
        ports={"22": "tcp"},
        ssh_password=ssh_password,
    )
    
    print("Fertig! Überprüfen Sie Ihre Bestellungen für SSH-Verbindungsdaten.")
    return best["id"]

rent_cheapest_rtx4090()
```

***

### Meine Bestellungen überwachen

```python
import time
from clore_client import CloreClient

client = CloreClient(api_key="YOUR_API_KEY")

def monitor_orders(poll_interval_seconds=60):
    """Bestellungen abfragen und Statusaktualisierungen ausgeben."""
    print(f"Überwache Bestellungen (Abfrage alle {poll_interval_seconds}s). Strg+C zum Beenden.\n")
    
    while True:
        orders = client.get_orders(include_completed=False)
        active = [o for o in orders if not o.get("expired")]
        
        print(f"--- {len(active)} aktive Bestellung(en) ---")
        for order in active:
            cluster = order.get("pub_cluster", [])
            tcp = order.get("tcp_ports", [])
            spend = order.get("spend", 0)
            
            ssh_info = ""
            if cluster and tcp:
                port = tcp[0].split(":")[1]
                ssh_info = f" | SSH: {cluster[0]}:{port}"
            
            print(f"  Bestellung {order['id']}: Server {order['si']}"
                  f" | ausgegeben {spend:.8f} BTC{ssh_info}")
        
        if not active:
            print("  Keine aktiven Bestellungen.")
        
        print()
        time.sleep(poll_interval_seconds)

monitor_orders()
```

***

### Automatisch mieten, wenn der Preis unter X fällt

```python
import time
from clore_client import CloreClient

client = CloreClient(api_key="YOUR_API_KEY")

def auto_rent_on_price_drop(
    gpu_model: str = "RTX 4090",
    max_price_btc: float = 0.00015,
    image: str = "cloreai/ubuntu20.04-jupyter",
    ssh_password: str = "SecurePass123",
    check_interval_seconds: int = 120,
):
    """
    Beobachten Sie den Marktplatz und mieten Sie automatisch eine GPU, wenn der Preis unter die Schwelle fällt.
    
    Parameter:
        gpu_model: GPU-Name, nach dem gesucht wird (case-insensitive)
        max_price_btc: Maximal akzeptabler Preis pro Tag in BTC
        image: Docker-Image, das bereitgestellt werden soll
        ssh_password: SSH-Passwort für den Container
        check_interval_seconds: Wie oft geprüft wird (beachten Sie die Rate-Limits!)
    """
    print(f"Suche nach {gpu_model} bei ≤ {max_price_btc:.8f} BTC/Tag...")
    
    while True:
        servers = client.list_servers()
        
        for server in servers:
            gpu = server["specs"].get("gpu", "")
            if gpu_model.lower() not in gpu.lower():
                continue
            if server["rented"]:
                continue
            
            price = server["price"]["on_demand"]["bitcoin"]
            if price <= max_price_btc:
                print(f"🎯 Treffer gefunden! Server {server['id']}: {gpu} @ {price:.8f} BTC/Tag")
                
                try:
                    client.create_order(
                        server_id=server["id"],
                        image=image,
                        order_type="on-demand",
                        currency="bitcoin",
                        ports={"22": "tcp"},
                        ssh_password=ssh_password,
                        required_price=price,  # Diesen Preis sperren
                    )
                    print(f"✅ Bestellung für Server {server['id']} erstellt!")
                    return server["id"]
                except Exception as e:
                    print(f"Failed to create order: {e}. Will retry...")
        
        print(f"No match yet. Checking again in {check_interval_seconds}s...")
        time.sleep(check_interval_seconds)

auto_rent_on_price_drop(gpu_model="4090", max_price_btc=0.00012)
```

***

## WebSocket

Die Clore.ai REST-API bietet derzeit keinen WebSocket-Endpunkt. Für Echtzeitüberwachung verwenden Sie Polling mit einem angemessenen Intervall (siehe Ratenbegrenzungen unten).

***

## Ratenbegrenzungen

| Endpunkt                        | Limit                       |
| ------------------------------- | --------------------------- |
| Die meisten Endpunkte           | **1 Anfrage/Sekunde**       |
| `create_order`                  | **1 Anfrage/5 Sekunden**    |
| `set_spot_price` (Preissenkung) | Einmal pro **600 Sekunden** |

**Antwort bei Erreichen des Ratenlimits (Code 5):**

```json
{ "code": 5 }
```

**Best Practices:**

* Fügen Sie hinzu `time.sleep(1)` zwischen aufeinanderfolgenden API-Aufrufen
* Für `create_order`, warten Sie mindestens 5 Sekunden zwischen Anfragen
* Verwenden Sie Polling-Intervalle von 60+ Sekunden für Überwachungs-Schleifen
* Cachen Sie Marktplatzdaten lokal, wenn Sie sie häufig abfragen müssen

**Python-Helfer:**

```python
import time

def safe_api_call(fn, *args, delay=1.1, **kwargs):
    """Wickelt einen API-Aufruf mit Ratenlimit-Sicherheit ein."""
    result = fn(*args, **kwargs)
    time.sleep(delay)
    return result
```

***

## Fehlerbehandlung

Jede API-Antwort enthält ein `code` Feld. Ein Wert von `0` bedeutet Erfolg.

### Fehlercodes

| Code | Bedeutung                              | Aktion                                          |
| ---- | -------------------------------------- | ----------------------------------------------- |
| `0`  | Erfolg                                 | —                                               |
| `1`  | Datenbankfehler                        | Nach einer Verzögerung erneut versuchen         |
| `2`  | Ungültige Eingabedaten                 | Überprüfen Sie Ihren Anfragekörper/Parameter    |
| `3`  | Ungültiges API-Token                   | Überprüfen Sie Ihren API-Schlüssel im Dashboard |
| `4`  | Ungültiger Endpunkt                    | Überprüfen Sie die Endpunkt-URL                 |
| `5`  | Ratenlimit überschritten (1 req/sec)   | Fügen Sie Verzögerungen zwischen Anfragen hinzu |
| `6`  | Anwendungsfehler (siehe `Fehler` Feld) | Lesen Sie das `Fehler` Feld für Details         |

### Code 6 Unterfehler

| `Fehler` Wert                 | Bedeutung                                                                                       |
| ----------------------------- | ----------------------------------------------------------------------------------------------- |
| `exceeded_max_step`           | Spot-Preisreduzierung zu groß; überprüfen Sie `max_step` Feld                                   |
| `can_lower_every_600_seconds` | Muss warten, bevor der Spot-Preis erneut gesenkt werden kann; überprüfen Sie `time_to_lowering` |

### Python Fehlerbeispiel zur Fehlerbehandlung

```python
from clore_client import CloreClient, CloreAPIError
import time

client = CloreClient(api_key="YOUR_API_KEY")

def create_order_with_retry(server_id, image, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.create_order(
                server_id=server_id,
                image=image,
                order_type="on-demand",
                currency="bitcoin",
                ports={"22": "tcp"},
                ssh_password="SecurePass123",
            )
        except CloreAPIError as e:
            if e.code == 5:  # Ratenlimit
                print(f"Rate limited. Waiting 5s... (attempt {attempt+1}/{max_retries})")
                time.sleep(5)
            elif e.code == 3:  # Ungültiger API-Schlüssel
                print("Ungültiger API-Schlüssel! Überprüfen Sie Ihren CLORE_API_KEY.")
                raise
            elif e.code == 2:  # Ungültige Eingabe
                print(f"Invalid request: {e}")
                raise
            else:
                print(f"API error: {e}. Retrying in 3s...")
                time.sleep(3)
    
    raise RuntimeError(f"Failed after {max_retries} attempts")
```

### JavaScript Fehlerbeispiel zur Fehlerbehandlung

```javascript
async function createOrderWithRetry(client, serverConfig, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.createOrder(serverConfig);
    } catch (err) {
      const code = parseInt(err.message.match(/error (\d+)/)?.[1]);
      
      if (code === 5) {
        console.log(`Rate limited. Waiting 5s... (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise(r => setTimeout(r, 5000));
      } else if (code === 3) {
        throw new Error('Invalid API key');
      } else {
        console.log(`API error: ${err.message}. Retrying in 3s...`);
        await new Promise(r => setTimeout(r, 3000));
      }
    }
  }
  throw new Error(`Failed after ${maxRetries} attempts`);
}
```

***

## Verfügbare Docker-Images

Clore.ai stellt vorgefertigte Images bereit, die für GPU-Workloads optimiert sind:

| Image                         | Beschreibung              |
| ----------------------------- | ------------------------- |
| `cloreai/ubuntu20.04-jupyter` | Ubuntu 20.04 + JupyterLab |
| `cloreai/ubuntu22.04-jupyter` | Ubuntu 22.04 + JupyterLab |

Sie können auch jedes öffentliche Docker Hub-Image verwenden. Für GPU-Zugriff verwenden Sie CUDA-fähige Images, z. B.:

```
nvidia/cuda:12.1.0-devel-ubuntu22.04
```

***

## Portweiterleitung

Geben Sie beim Erstellen einer Bestellung Ports an, die expose werden sollen:

```json
{
  "ports": {
    "22": "tcp",
    "8888": "http",
    "6006": "http"
  }
}
```

* **`"tcp"`** — Direkte TCP-Portweiterleitung (für SSH, benutzerdefinierte Server)
* **`"http"`** — HTTPS-Proxy (für Jupyter, Web-UIs). Es ist nur ein HTTP-Port pro `http_port` Feld erlaubt.

Nach Erstellung der Bestellung erscheinen Verbindungsdetails in `my_orders`:

```json
{
  "pub_cluster": ["n1.c1.clorecloud.net", "n2.c1.clorecloud.net"],
  "tcp_ports": ["22:10000"],
  "http_port": "8888"
}
```

Verbinden über SSH:

```bash
ssh root@n1.c1.clorecloud.net -p 10000
```

Zugriff auf Jupyter über den Browser: `https://n1.c1.clorecloud.net` (verwendet das `http_port`)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/clore.ai/clore.ai-eng-de/entwickler/cli-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
