Metadata-Version: 2.4
Name: observable-code
Version: 0.1.2
Summary: Instant observability for developers shipping fast
Requires-Python: >=3.8
Requires-Dist: qrcode>=7.4
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: responses>=0.23.0; extra == 'dev'
Description-Content-Type: text/markdown

# observable-code

Instant observability for developers shipping fast. Add one decorator, scan a QR code, and watch your background jobs appear on your phone in real time.

## Install

```bash
pip install observable-code
```

## Quick start

### 1. Pair your phone

```bash
observable setup
```

This creates a session, shows a QR code in your terminal, and saves your session ID to `~/.observable-code/config.json` once you scan it with the [Observable Code app](https://obs.code).

### 2. Add the decorator

```python
import observable

@observable.track("daily_backup")
def run_backup():
    # your code here
    pass
```

Run your function — the event appears on your phone instantly.

---

## API

### `@observable.track(name)` — decorator

Wraps a sync or async function. Automatically captures:
- Status: `completed` or `failed`
- Duration in milliseconds
- On exception: error type and message (exception is re-raised)

```python
# Sync
@observable.track("send_report")
def send_report():
    ...

# Async
@observable.track("fetch_prices")
async def fetch_prices():
    ...

# Context manager
with observable.track("batch_import"):
    process_records()
```

### `observable.trace(key, value)`

Attach structured data to the next `track()` call in the current thread. Values are cleared after each tracked call.

```python
observable.trace("records_processed", 500)
observable.trace("source", "s3://my-bucket")

@observable.track("nightly_sync")
def sync():
    ...
# event metadata → {"records_processed": 500, "source": "s3://my-bucket"}
```

### `observable.event(name, status="completed", **kwargs)`

Post a manual event without wrapping a function. Keyword arguments become event metadata.

```python
observable.event("payment_received", amount=99.99, currency="USD")
observable.event("webhook_failed", status="failed", endpoint="/stripe")
```

---

## CLI

```
observable setup      First-time pairing wizard
observable pair       Re-pair with a new phone
observable show-key   Print current session ID
observable status     Check if the current session is paired
```

---

## Configuration

Credentials are resolved in this order:

| Source | How to set |
|---|---|
| `OBSERVABLE_CODE_SESSION_ID` env var | `export OBSERVABLE_CODE_SESSION_ID=<uuid>` |
| Config file | `observable setup` writes `~/.observable-code/config.json` |

The API URL defaults to `http://localhost:3000` and can be overridden:

```bash
export OBSERVABLE_CODE_API_URL=https://your-deployment.vercel.app
```

If no credentials are found, events are silently skipped and a one-time hint is printed to stderr — your code always continues running.

---

## Behaviour

- **Fire-and-forget**: events are posted in a background daemon thread — your function's return is never delayed
- **Timeout**: 2 seconds per HTTP request
- **Failures**: silently swallowed (a warning is printed to stderr)
- **Process exit**: never blocked by a pending post
