Metadata-Version: 2.5
Name: nocville
Version: 0.2.3
Summary: Nocville Status Pusher SDK for Python
Project-URL: Homepage, https://nocville.com
Author: Nocville Team
License-Expression: MIT
License-File: LICENSE
Keywords: monitoring,noc,nocville,status
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Provides-Extra: async
Requires-Dist: aiohttp>=3.8.0; extra == 'async'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# nocville

Python SDK for pushing status updates to Nocville.

## Installation

```bash
pip install nocville
```

For async support:

```bash
pip install nocville[async]
```

## Usage

### Synchronous

```python
from nocville import NocvilleClient, Status, Mood

client = NocvilleClient(
    api_key="your-api-key",
    service_id="my-service",
)

# Push a status update
response = client.push_status(
    status=Status.HEALTHY,
    mood=Mood.HAPPY,
    message="All systems operational",
    metrics={"cpu": 45, "memory": 60},
)

# Quick helpers
client.healthy("Everything is fine")
client.warning("High memory usage")
client.critical("Service down!")
```

### Asynchronous

```python
import asyncio
from nocville.client import AsyncNocvilleClient, Status

async def main():
    client = AsyncNocvilleClient(
        api_url="https://your-nocville-server.com",
        api_key="your-api-key",
        service_id="my-service",
    )

    response = await client.push_status(
        status=Status.HEALTHY,
        message="All systems operational",
    )

asyncio.run(main())
```

## Configuration

| Option        | Type  | Default                | Description                                 |
| ------------- | ----- | ---------------------- | ------------------------------------------- |
| `api_key`     | str   | required               | Team API key for authentication             |
| `api_url`     | str   | `https://nocville.com` | Nocville API URL (override for development) |
| `service_id`  | str   | required               | The service ID this client represents       |
| `timeout`     | float | 5.0                    | Request timeout in seconds                  |
| `retries`     | int   | 3                      | Number of retry attempts on failure         |
| `retry_delay` | float | 1.0                    | Base delay between retries in seconds       |
| `debug`       | bool  | False                  | Enable debug logging                        |

## Status Values

- `Status.HEALTHY` - Service is operating normally
- `Status.WARNING` - Service has non-critical issues
- `Status.CRITICAL` - Service has critical issues
- `Status.UNKNOWN` - Service status cannot be determined

## Mood Values

- `Mood.HAPPY` - NPC appears happy
- `Mood.CONCERNED` - NPC appears concerned
- `Mood.STRESSED` - NPC appears stressed
- `Mood.IDLE` - NPC appears neutral

## License

MIT

## Maps, AI coworkers and shutdown

Both clients accept `map="Ops"` (map name or ID) and the same keyword-only status fields:

```python
from nocville import NocvilleClient, BehaviorHints, CompanionSpec

client = NocvilleClient(api_key="your-api-key", service_id="build-agent", map="Ops")
behavior: BehaviorHints = {"activity": "busy", "busy": "seek-desk"}
companion: CompanionSpec = {
    "screenType": "desk-sign",
    "placement": "on-desk",
    "size": {"w": 3, "h": 1},
    "label": "checkout-api",
}
try:
    client.push_status(
        "healthy", name="Claude", message="Running tests",
        behavior=behavior, companion=companion,
        appearance={"family": "claude"},
        agent_stats={"added": 12, "removed": 3},
    )
finally:
    client.shutdown()
```

`agent_stats` becomes `agentStats` on the wire. Behavior and companion dictionaries use
API camelCase keys. `offline=True` immediately removes the NPC. `shutdown()` stops and
drains heartbeats before sending an unknown/idle/offline sentinel, and returns a
`PushResponse`; use `await client.shutdown()` for `AsyncNocvilleClient`.

`auto_offline=True` opts into a best-effort interpreter-exit callback (default `False`).
For async clients this callback uses a synchronous request because the event loop may
already be closed. Prefer explicit shutdown in `finally`; exit callbacks cannot run after
SIGKILL or a crash. Python 3.9 or newer is required.
