Metadata-Version: 2.4
Name: marona
Version: 0.1.3
Summary: Official Python client SDK for Marona-compatible AI runtimes and Hub integrations.
Author: Blessing Nyuwani
License-Expression: MIT
Project-URL: Homepage, https://www.marona.ai
Project-URL: Repository, https://github.com/BlessingNyuwani/edge-node-service
Project-URL: Documentation, https://hub.marona.ai
Keywords: marona,sdk,mcp,agents,ai,runtime
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
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.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0,>=0.28
Provides-Extra: dev
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
Requires-Dist: build<2.0,>=1.2; extra == "dev"
Requires-Dist: twine<7.0,>=6.0; extra == "dev"
Dynamic: license-file

# marona

Official Python client SDK for Marona-compatible AI runtimes and Hub integrations.

Marona provides a simple interface for messaging, discovering apps, managing
permissions, connecting service accounts, receiving inbound events, and using
tools from any compatible runtime.

You can use it with Marona Cloud or with your own self-hosted Marona-compatible
runtime.

`marona` has two separate surfaces:

- `client.hub.connect(...)`: free Hub integration for developers using their own agents.
- `client.runtime.message(...)`: paid Marona Runtime usage billed from Marona Balance.

## Install

```bash
pip install marona
```

## Send a message

```python
import asyncio

from marona import Marona


async def main() -> None:
    client = Marona(
        base_url="https://edge.marona.ai",
        api_key="mrn_live_...",
    )
    response = await client.message(
        "Create a PowerPoint about AI introduction",
        interface="api",
        conversation_id="demo",
    )
    print(response.reply)
    for artifact in response.artifacts:
        print(artifact.get("filename"))


asyncio.run(main())
```

## Connect Hub Apps

Use Hub integration when your own agent framework will execute the model and you
only need approved app connections from Marona Hub.

```python
import asyncio

from marona import Marona


async def main() -> None:
    marona = Marona(api_key="mrn_live_...")
    servers = await marona.hub.connect(
        ["gmail", "calendar"],
        adapter="openai_mcp",
    )
    print(servers)


asyncio.run(main())
```

For frameworks that need tool descriptors instead of MCP server configs:

```python
tools = await marona.hub.connect(["billing-mcp"], adapter="tools")
```

Hub integration does not call Marona Runtime and does not use Marona Balance.

## Edge runtimes

Device runtimes can activate and sync against Marona Hub with the same official
package:

```python
from marona import EdgeHubClient

edge = EdgeHubClient(hub_url="https://hub.marona.ai")
activation = await edge.start_activation(
    device_name="Office runtime",
    device_public_key="...",
)
```

## Add balance

Developers add balance by paying a money amount. The runtime metering unit stays
internal so your application does not depend on any provider token pricing.

```python
payment = await marona.hub.create_payment_intent(
    amount_cents=1000,
    payment_method="ecocash",
    ecocash_number="0771234567",
)
print(payment["payment_intent"]["amount_cents"])
```

## Runtime targets

Marona Cloud:

```python
from marona import Marona

client = Marona(
    base_url="https://edge.marona.ai",
    api_key="mrn_live_...",
)
```

Self-hosted:

```python
from marona import Marona

client = Marona(
    base_url="https://my-company-edge.example.com",
    api_key="mrn_live_...",
)
```

## List available apps

```python
import asyncio

from marona import Marona


async def main() -> None:
    client = Marona("https://edge.marona.ai")
    apps = await client.list_apps(limit=20, search="slides")

    for app in apps.apps:
        print(app["slug"], app["name"])


asyncio.run(main())
```

## Pair an interface with WhatsApp

```python
import asyncio

from marona import Marona


async def main() -> None:
    client = Marona("https://edge.marona.ai")
    pairing = await client.start_pairing(interface="web", device_name="My web app")

    print(pairing.display_code)
    print(pairing.whatsapp_url)


asyncio.run(main())
```

After the user sends the pairing code from WhatsApp, poll for completion:

```python
status = await client.pairing_status(pairing.pairing_id)

if status.identity:
    identity_token = status.identity.access_token
```

Use the identity token on future requests:

```python
response = await client.message(
    "What is on my calendar today?",
    identity_token=identity_token,
)
```

## Connect an app account

Some apps need a user service connection, such as Gmail, Google Calendar, or
Google Slides.

```python
import asyncio

from marona import Marona


async def main() -> None:
    client = Marona("https://edge.marona.ai")
    identity_token = "mrn_identity_token_from_pairing"
    connection = await client.connect_app(
        "slides",
        provider="google",
        identity_token=identity_token,
        return_url="https://example.com/connected",
    )

    print(connection.authorization_url)


asyncio.run(main())
```

Open `authorization_url` in a browser so the user can authorize the provider.

## Attach files

```python
import asyncio

from marona import Marona, RuntimeAttachment


async def main() -> None:
    identity_token = "mrn_identity_token_from_pairing"
    attachment = RuntimeAttachment.from_bytes(
        b"hello",
        filename="note.txt",
        mime_type="text/plain",
        kind="document",
    )

    client = Marona("https://edge.marona.ai")
    response = await client.message(
        "Summarize this file",
        attachments=[attachment],
        identity_token=identity_token,
    )
    print(response.reply)


asyncio.run(main())
```

## Advanced

`EdgeRuntimeClient` is kept as a compatibility class for existing integrations.
New integrations should import `Marona`.

## Release

Build and verify the package before uploading:

```bash
rm -rf dist build marona.egg-info
python3 -m build
python3 -m twine check dist/*
```

Upload with the project-scoped PyPI token for `marona`:

```bash
TWINE_USERNAME=__token__ TWINE_PASSWORD='PROJECT_SCOPED_TOKEN' python3 -m twine upload dist/*
```

Use a project-scoped token for normal releases. Account-wide tokens should only
be used for the first upload of a new PyPI project.
