Metadata-Version: 2.4
Name: sail-sdk
Version: 0.1.0
Summary: Python SDK for the Sail sandbox platform
Project-URL: Homepage, https://app.sailresearch.com
Project-URL: Repository, https://github.com/sailresearch/sail
Project-URL: Issues, https://github.com/sailresearch/sail/issues
Author: Sail
License-Expression: Apache-2.0
Keywords: grpc,sail,sailbox,sandbox,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: grpcio>=1.80.0
Requires-Dist: protobuf>=6.31.1
Provides-Extra: dev
Requires-Dist: grpcio-tools>=1.80.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# sail-sdk

Python SDK for the Sail sandbox platform.

## Install

```bash
pip install sail-sdk
```

```bash
uv add sail-sdk
```

## Sailbox exec

```python
import sail

app = sail.App.find(name="example-app", mint_if_missing=True)
sb = sail.Sailbox.create(
    image=sail.Image.debian_amd64,
    app=app,
    name="sandbox-1",
)
result = sb.exec("echo hi", timeout=5).wait()

print(result.stdout)
print(result.stderr)
print(result.returncode)

# Detached background launch. wait() waits for the launcher shell, not for the
# long-lived background process to exit.
sb.exec("python3 -m http.server 3000", background=True).wait()

# Omitting timeout means Sail will not terminate the exec automatically.
sb.exec("sleep 600").wait()
```

## Sailbox networking

```python
import sail

app = sail.App.find(name="example-app", mint_if_missing=True)
sb = sail.Sailbox.create(
    image=sail.Image.debian_amd64,
    app=app,
    name="sandbox-net",
    timeout=300,
    ingress_ports=[3000],
)

listener = sb.listener(3000)
print(listener.url)

req = sb.request(
    "POST",
    "https://example.com/api",
    json={"hello": "world"},
    idempotency_key="example-1",
)
print(req.id, req.status)

completed = req.wait()
print(completed.status)
print(completed.response.status_code)
print(completed.response.text)
```

Explicit base images:

```python
amd64_image = sail.Image.debian_amd64
amd_image = sail.Image.debian_amd
arm64_image = sail.Image.debian_arm64
arm_image = sail.Image.debian_arm
```

## Examples

- `examples/sailbox_smoke.py`: start a sailbox from the amd64 Debian image.
- `examples/sailbox_custom_image.py`: build a custom image with `apt_install`, `pip_install`, `run_commands`, and `env`, then launch a sailbox from it.

## Publishing

Build a distributable package locally from the repo root:

```bash
just python-sdk-build
```

Publish from a developer machine with a PyPI token:

```bash
export UV_PUBLISH_TOKEN=pypi-...
just python-sdk-publish
```

The repository also includes a GitHub Actions release workflow at `.github/workflows/python-sdk-publish.yml`.
It publishes when you push a tag like `python-sdk-v0.1.0`, after verifying that the tag version matches `sail.__version__`.

Recommended setup:

1. Create the `sail-sdk` project on PyPI.
2. Configure PyPI Trusted Publishing for this GitHub repository and the `python-sdk-publish.yml` workflow.
3. Bump `sdk/python/src/sail/__about__.py`.
4. Push a matching tag: `git tag python-sdk-v0.1.0 && git push origin python-sdk-v0.1.0`
