Metadata-Version: 2.5
Name: render-lab-triggers
Version: 0.1.0
Summary: HTTP, webhook, and cron dispatch for Render workflows
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: render==1.0.1
Requires-Dist: uvicorn<1,>=0.35
Description-Content-Type: text/markdown

# render-lab-triggers

Registration-free HTTP, webhook, and cron dispatch to a separate Render Workflow.

```python
from render_lab_triggers import create_dispatch_server

app = create_dispatch_server()
# uvicorn main:app --host 0.0.0.0 --port 3000
```

`GET /healthz` returns `ok`. `POST /tasks/{namespace.task}` requires
`Authorization: Bearer <DISPATCH_TOKEN>` and accepts JSON positional arguments
(an array), a single JSON value, or an empty body (`[{}]`). It returns HTTP 202
with `runId`. Add `?wait=1` for a bounded wait: HTTP 200 with status/results on
completion or HTTP 202 when the wait expires. The default wait is 25 seconds.

`create_dispatch_server` accepts `workflow_slug`, `token`, `dispatcher`,
`webhooks`, `wait_timeout_ms`, and `max_body_bytes`. Bodies are capped at 1 MiB
by counting streamed bytes, regardless of Content-Length. Oversized bodies
return 413 before signature verification. The ASGI server owns connection cleanup.

Vendor adapters live in `render_lab_tasks_<vendor>.webhooks`; mount them in the
`webhooks` mapping at `/webhooks/{name}`. Their `verify` function sees the exact
raw UTF-8 body and lowercase headers before JSON parsing. `map` returns a
`{"task": ..., "args": [...]}` dispatch or `None` to acknowledge and ignore.
As in the pinned TS server, webhook bodies must be JSON. Twilio form-encoded
signatures can be verified with its standalone adapter, but form dispatch needs
a custom endpoint that decodes the form after verification.

```python
from render_lab_triggers import run_cron

# In an async cron entry point:
# result = await run_cron(task="report.daily", args=[{}])
```

`run_cron` defaults to asynchronous dispatch. `wait=True` waits at most five
minutes; `wait_timeout_ms` overrides it. `render_dispatcher(slug=...)` starts via
the pinned Render SDK and consumes its public SSE event iterator. It closes
streams on completion, timeout, and cancellation, without adding retries.
`client=` injects a RenderAsync-compatible client; the caller owns that client.
`serve_dispatch_server(port=..., **options)` runs Uvicorn until shutdown.

## Environment

- `WORKFLOW_SLUG`: target Workflow slug, unless provided explicitly.
- `DISPATCH_TOKEN`: bearer token for `/tasks/*`; unset rejects all such requests.
- `RENDER_API_KEY`: Render SDK credential, read when dispatching.
- `CRON_TASK`: default cron task name.
- `CRON_INPUT`: JSON cron input; an array is positional arguments.
- `PORT`: serving port, default 3000.

Imports never read credentials or register tasks. Tests inject dispatchers and
SDK event streams; live cross-service dispatch remains in the testing backlog.

## Installation

```sh
pip install render-lab-triggers==0.1.0
```
