Metadata-Version: 2.5
Name: herdr-python-sdk
Version: 0.1.0
Summary: Independent typed sync and async clients for Herdr socket protocol 22
Project-URL: Source, https://github.com/rudironsoni/herdr-python-sdk
Project-URL: Issues, https://github.com/rudironsoni/herdr-python-sdk/issues
Project-URL: Changelog, https://github.com/rudironsoni/herdr-python-sdk/blob/main/CHANGELOG.md
Author: Rudimar Ronsoni
License-Expression: Apache-2.0
License-File: LICENSE
License-File: schema/LICENSE
License-File: schema/NOTICE
Keywords: asyncio,herdr,pydantic,sdk,terminal
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic<3,>=2.12
Description-Content-Type: text/markdown

# herdr-python-sdk

An independent Python 3.12+ SDK for Herdr socket protocol 22. The package provides
synchronous and asynchronous calls, typed Pydantic models, event subscriptions, and
graphics streams. It uses Unix sockets on macOS and Linux and native named pipes on
Windows. This project is not maintained by the Herdr project.

## Install

```sh
python -m pip install herdr-python-sdk
```

Herdr 0.9.0 or another server that implements socket protocol 22 must already be running.

```python
from herdr_sdk import __version__

print(__version__)
```

## Read workspaces

```python
from herdr_sdk import HerdrClient, models

with HerdrClient() as client:
    result = client.workspace_list()
    if isinstance(result, models.WorkspaceListResponse):
        for workspace in result.workspaces:
            print(workspace)
```

```python
import asyncio
from herdr_sdk import AsyncHerdrClient, models

async def main():
    async with AsyncHerdrClient(session="default") as client:
        result = await client.workspace_list()
        if isinstance(result, models.WorkspaceListResponse):
            print(result.workspaces)

asyncio.run(main())
```

Socket method names use underscores in Python: `pane.read` becomes `pane_read`.
All 102 schema methods are declared in
[client.py](https://github.com/rudironsoni/herdr-python-sdk/blob/main/src/herdr_sdk/client.py).
Use the parameter classes in
[models.py](https://github.com/rudironsoni/herdr-python-sdk/blob/main/src/herdr_sdk/models.py):

```python
with HerdrClient() as client:
    result = client.pane_read(
        models.PaneReadParams(pane_id="w1:p1", source=models.ReadSource.recent)
    )
    if isinstance(result, models.PaneReadResponse):
        print(result.read.text)
```

Methods return the schema's result union. Check the response class before using
its fields. Server fields unknown to the pinned schema remain in `model_extra`.
For dictionary inputs, use
`client.request("pane.read", {"pane_id": "w1:p1", "source": "recent"})`.
Only protocol 22 method names are accepted.

## Select a session

Pass either `socket_path` or `session`; passing both raises `ValueError`.
With neither set, `HERDR_SOCKET_PATH` takes precedence, followed by
`HERDR_SESSION` (default: `default`). Session names contain 1 to 64 ASCII letters,
digits, dots, underscores, or hyphens; `.` and `..` are invalid.

Discovery uses `XDG_CONFIG_HOME/herdr`, then the platform's user config directory.
The default session uses `herdr.sock`; named sessions use
`sessions/<session>/herdr.sock`. On Windows, pass Herdr's socket identity, not an
already prefixed pipe name. The transport adds `\\.\pipe\`.
Windows async callers must use `asyncio.ProactorEventLoop`.

## Events

```python
from herdr_sdk import HerdrClient, models

with HerdrClient() as client:
    params = models.EventsSubscribeParams(
        subscriptions=[models.WorkspaceRenamedSubscription()]
    )
    with client.events_subscribe(params, timeout=60) as events:
        for event in events:
            print(event.event, event.data)
```

Async callers use `async with await client.events_subscribe(params)` and
`async for event in events`. Both ordinary events and dotted subscription events
return typed envelopes. Leaving the context closes the subscription.

## Graphics streams

```python
from herdr_sdk import HerdrClient, GraphicsFrame, GraphicsStreamParams, models

frame = GraphicsFrame(
    format=models.PaneGraphicsFormat.rgba, image_width=1, image_height=1
)
with HerdrClient() as client:
    with client.pane_graphics_stream(GraphicsStreamParams(pane_id="w1:p1")) as stream:
        stream.send_frame(frame, b"\xff\x00\x00\xff")
```

`pane.graphics.stream` is public in Herdr 0.9.0 but absent from its JSON schema.
The SDK implements its JSON headers and raw byte payloads directly. Inline frames
are limited to 16 MiB. Inline success has no server acknowledgement, so a completed
send proves transmission only. Subsequent operations report received server errors.
Closing the stream releases its layer.

For file frames, call
`stream.send_file(frame, path, sequence=1, revision=1)`. The file must contain raw
RGBA or BGRA pixels and remain unchanged until the matching acknowledgement
returns. The SDK checks its request ID, sequence, and revision. It does not create
or remove the file. After a failed call, do not assume the server has released it.
Async streams provide the same methods with `await`.

## Timeouts, errors, and lifetime

Each operation first checks `ping` for protocol 22. A mismatch raises
`HerdrProtocolMismatchError` before sending the requested action. Ordinary calls
use one connection each after the preflight. There is no reconnect or replay.

The connect timeout is 5 seconds. The ordinary request and preflight timeout is
30 seconds. Set `timeout=None` for no read deadline or pass a positive timeout
per method. Each preflight has its own deadline. Waiting methods (`agent.start`,
`agent.wait`, `events.wait`, `pane.wait_for_output`, and `agent.prompt` with `wait`)
have no read deadline by default. Subscriptions use the client timeout for their
initial acknowledgement and no deadline for later events unless one is supplied.
Responses default to a 32 MiB limit; use `max_response_bytes` to change it.

`HerdrAPIError` retains `code`, `message`, and `request_id`. Transport, timeout,
invalid-response, and version failures have separate `HerdrError` subclasses.
Invalid caller parameters raise Pydantic `ValidationError` or `ValueError`.

Keep each async client on one event loop and each sync client on one thread.
Use the async client inside an active event loop. Context managers close sockets
and streams; closing an async client also cancels its active requests.

Enable the `herdr_sdk` logger at `DEBUG` to see method names, target IDs, request
IDs, byte counts, and timing. Logs do not include terminal text or frame contents.

## Source and development

The schema and stream contract are pinned to
[Herdr 0.9.0, commit b99002ac99b09e00b4ca692436cb15a6b0d676f1](https://github.com/herdrdev/herdr/tree/b99002ac99b09e00b4ca692436cb15a6b0d676f1).
See the official [Herdr socket API](https://herdr.dev/docs/socket-api/).
The vendored schema carries the upstream
[Apache 2.0 license](https://github.com/rudironsoni/herdr-python-sdk/blob/main/schema/LICENSE).
`client_shell.surface.set` is exposed because it is in the schema, but the normal
socket server rejects it with `connection_local_only`.

SDK versions follow [Semantic Versioning](https://semver.org/) independently of Herdr
versions. See the [changelog](https://github.com/rudironsoni/herdr-python-sdk/blob/main/CHANGELOG.md)
for protocol compatibility and release details.

Install this checkout for development:

```sh
python -m pip install .
```

```sh
uv sync --locked
uv run --locked python scripts/generate.py --check
uv run --locked ruff check src scripts tests
uv run --locked ruff format --check src scripts tests
uv run --locked mypy src
uv run --locked pytest -q
uv build --no-sources
```

To rebuild the committed declarations after a deliberate schema update, run
`uv run --locked python scripts/generate.py`. Generation uses the vendored schema and the
locked vendor model generator. Tests use an independent local socket server.
The GitHub Actions matrix covers Python 3.12, 3.13, and 3.14 on macOS, Linux, and
Windows; a configured matrix is not evidence that those jobs have run.
