Metadata-Version: 2.4
Name: cubehosting
Version: 0.1.0
Summary: SDK oficial da API da Cube Hosting para Python.
License: Proprietary
Project-URL: Documentation, https://docs.cubehosting.com.br/sdk/python
Project-URL: Homepage, https://cubehosting.com.br
Keywords: cube-hosting,discord-bot,hosting,sdk
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# cubehosting

SDK oficial da [API da Cube Hosting](https://docs.cubehosting.com.br/api-reference/introduction) para Python 3.9 ou mais novo. Sem dependências: só a biblioteca padrão.

```bash
pip install cubehosting
```

## Começando

Crie uma chave em [Chaves de API](https://app.cubehosting.com.br/api-keys) e guarde na variável `CUBE_API_KEY`.

```python
from cubehosting import Cube

cube = Cube()  # lê a chave de CUBE_API_KEY; ou Cube("cube_…")

projects = cube.list_projects()["projects"]
cube.restart_project(projects[0]["id"])
```

Cada método é uma rota da API, com o nome da [referência](https://docs.cubehosting.com.br/api-reference/introduction) em snake_case (`list_projects`, `start_project`, `get_project_metrics`…): primeiro os IDs do caminho, depois os campos como argumentos nomeados em snake_case (`memory_mb` vai como `memoryMb`). A resposta é o JSON da API, com os campos como ela manda (`memoryMb`, `createdAt`).

## Enviar código

```python
import os

created = cube.create_project(
    "bot.zip",  # o caminho, os bytes ou um arquivo aberto em "rb"
    language="python",
    command="python bot.py",
    variables={"DISCORD_TOKEN": os.environ["DISCORD_TOKEN"]},
    start=True,
)

# Depois, só o código novo:
cube.upload_project_code(created["project"]["id"], "bot.zip")
```

## Logs ao vivo

```python
for event in cube.stream_project_logs(project_id, lines=100):
    if event["event"] == "line":
        print(event["data"]["text"])
    elif event["data"]["status"] == "crash_loop":
        break
```

`follow=False` manda só as últimas linhas e termina. `source="build"` mostra a instalação.

## Blob

O `upload_blob` pede o envio, manda os bytes direto ao armazenamento (em partes de 16 MiB acima disso, lendo do disco aos pedaços) e confirma:

```python
obj = cube.upload_blob("backups/2026-10-01.tar.gz", "backup.tar.gz")
cube.download_blob(obj["id"], "copia.tar.gz")
```

O link (o `publicUrl` ou o de `create_blob_download_url`) abre com qualquer cliente HTTP, inclusive o `urllib.request.urlopen` puro: o `download_blob` só junta os dois passos.

Se uma parte não chega, o erro é um `CubeUploadError` com o `object_id`: chame `upload_blob(path, data, object_id=...)` para mandar só o que falta (vale por 24 horas).

## Arquivos do projeto

Com uma chave com as permissões de Arquivos (`files:read` para ler e `files:write` para mudar), nos planos pagos. O `compare_files` diz o que mudou (pelo SHA-256) sem mudar nada; o `upload_file` manda um arquivo de até 500 MB em partes de 16 MiB, lendo do disco aos pedaços; o `apply_project_changes` faz valer no projeto no ar:

```python
import hashlib
from pathlib import Path

data = Path("dist/bot.py").read_bytes()
compared = cube.compare_files(
    project_id,
    files=[{"path": "dist/bot.py", "size_bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}],
)
# blocked (caminho protegido ou inválido) não se envia, como na aba Arquivos
if compared["files"][0]["status"] in ("new", "changed"):
    cube.upload_file(project_id, "dist/bot.py", data, should_overwrite=True)
    cube.apply_project_changes(project_id)

print(cube.read_file(project_id, path="requirements.txt")["content"])
```

Também `list_files`, `write_file`, `search_files`, `download_file`, `download_files_zip`, `move_file` (`from_path=`, `to=`), `move_files`, `create_folder`, `delete_file` e `delete_files`.

## Erros

Toda resposta de erro vira um `CubeApiError`, com o `code` fixo em inglês e a mensagem em português:

```python
from cubehosting import CubeApiError

try:
    cube.start_project(project_id)
except CubeApiError as error:
    if error.code != "plan_limit_reached":
        raise
    print(error, error.body["freeMemoryMb"])
```

- `status`, `code`, `message` e `body` (com os campos extras de cada código); `retry_after_seconds` no `429`; `required_scope` no `insufficient_scope` (a permissão que falta na chave); `docs_url` com o que fazer.
- `CubeConnectionError`: sem conexão ou tempo esgotado.
- A chave nunca aparece no `repr`, no `print` nem em erro nenhum. O SDK não segue redirecionamento, para a chave nunca ir a outro endereço.

Documentação completa: [docs.cubehosting.com.br/sdk/python](https://docs.cubehosting.com.br/sdk/python).
