Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG VARIANT="3.9"
ARG VARIANT="3.10"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}

USER vscode
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/ai-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Required AI review of every non-draft PR into the staging SDK. Posts the
# `ai-review` status. See the reusable workflow for details.
#
# This file is hand-written custom code, not Stainless output: stlc seals it
# with the rest of the custom code and promote copies it to the production
# repo, where the job is skipped. Hand-written changes reach the SDK through
# PRs on the staging repo only.
#
# pull_request_target runs this file from the base branch, so a PR cannot
# edit or delete its own review.
#
# The reusable workflow is pinned to a commit, so a change to it only takes
# effect here through a reviewed PR that bumps the SHA.

name: AI Review

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write
statuses: write

jobs:
ai-review:
if: ${{ endsWith(github.repository, '-staging') }}
uses: mirurobotics/.github/.github/workflows/ai-review.yml@78e2fba753ddf238696af92184931ee08d3b0016
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.9.18
3.10.16
5 changes: 1 addition & 4 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
configured_endpoints: 9
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/miru-ml/miru-device-121ae8bfd5e26da5279f9daa66e49893b1a642ae1538487f6074d1f691da0f3a.yml
openapi_spec_hash: 59000babd0ead7d1721256e273894119
config_hash: 44423e70a225b0f8b4bb61fbe1a51afc
configured_endpoints: 11
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/miru-device-sdk.svg?label=pypi%20(stable))](https://pypi.org/project/miru-device-sdk/)

The Miru Python library provides convenient access to the Miru REST API from any Python 3.9+
The Miru Python library provides convenient access to the Miru REST API from any Python 3.10+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).

Expand Down Expand Up @@ -33,6 +33,37 @@ device = client.device.retrieve()
print(device.id)
```

## Connecting to the agent

The client talks to the Miru Agent running on the same device:

- **Linux** (default): over the agent's Unix socket, `/run/miru/miru.sock`
(`socket_path` or `MIRU_AGENT_SOCKET`). The socket needs no token; access is
limited to the `miru` group.
- **Windows** (default), or `agent_transport="tcp"` / `MIRU_AGENT_TRANSPORT=tcp`:
over loopback TCP with a bearer token. The agent publishes its port and token
in a discovery file, `%ProgramData%\Miru\device-api\device-api.json` on
Windows and `/run/miru/device-api.json` on Linux (`discovery_file` or
`MIRU_AGENT_DISCOVERY_FILE`). The client reads it and picks up the new token
and port whenever the agent restarts. On Windows the account must be an
administrator or a member of the local `Miru Agent Users` group to read the file.

You don't need to set `bearer_token` (`MIRU_AGENT_TOKEN`): the token changes
every time the agent starts. Setting it turns off the discovery file, and you
must then also set `base_url` to the agent's address.

If you pass your own `http_client` over TCP, wrap its transport in
`miru_device_sdk.lib.device_api.DiscoveryTransport` (or `AsyncDiscoveryTransport`)
so the client also picks up a new port after the agent restarts.

If the discovery file is missing, requests raise
`miru_device_sdk.lib.device_api.DiscoveryError`; if the agent doesn't answer,
`miru_device_sdk.APIConnectionError`.

```python
client = Miru(agent_transport="tcp")
```

## Async usage

Simply import `AsyncMiru` instead of `Miru` and use `await` with each API call:
Expand Down Expand Up @@ -344,7 +375,7 @@ print(miru_device_sdk.__version__)

## Requirements

Python 3.9 or higher.
Python 3.10 or higher.

## Contributing

Expand Down
30 changes: 24 additions & 6 deletions api.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# Shared Types

```python
from miru_device_sdk.types import DeploymentDeployed, DeploymentRemoved, Event
```

# Agent

Types:
Expand Down Expand Up @@ -43,6 +37,18 @@ Methods:
- <code title="get /device">client.device.<a href="./src/miru_device_sdk/resources/device.py">retrieve</a>() -> <a href="./src/miru_device_sdk/types/device_retrieve_response.py">DeviceRetrieveResponse</a></code>
- <code title="post /device/sync">client.device.<a href="./src/miru_device_sdk/resources/device.py">sync</a>() -> <a href="./src/miru_device_sdk/types/device_sync_response.py">DeviceSyncResponse</a></code>

# FileRules

Types:

```python
from miru_device_sdk.types import FileRule
```

Methods:

- <code title="get /file_rules/{file_rule_id}">client.file_rules.<a href="./src/miru_device_sdk/resources/file_rules.py">retrieve</a>(file_rule_id) -> <a href="./src/miru_device_sdk/types/file_rule.py">FileRule</a></code>

# GitCommits

Types:
Expand All @@ -55,6 +61,18 @@ Methods:

- <code title="get /git_commits/{git_commit_id}">client.git_commits.<a href="./src/miru_device_sdk/resources/git_commits.py">retrieve</a>(git_commit_id) -> <a href="./src/miru_device_sdk/types/git_commit.py">GitCommit</a></code>

# Events

Types:

```python
from miru_device_sdk.types import DeploymentDeployed, DeploymentRemoved, Event
```

Methods:

- <code title="get /events">client.events.<a href="./src/miru_device_sdk/resources/events.py">stream</a>(\*\*<a href="src/miru_device_sdk/types/event_stream_params.py">params</a>) -> <a href="./src/miru_device_sdk/types/event.py">Event</a></code>

# Releases

Types:
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ dependencies = [
"sniffio",
]

requires-python = ">= 3.9"
requires-python = ">= 3.10"
classifiers = [
"Typing :: Typed",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -129,7 +128,7 @@ filterwarnings = [
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
typeCheckingMode = "strict"
pythonVersion = "3.9"
pythonVersion = "3.10"

exclude = [
"_dev",
Expand Down
26 changes: 6 additions & 20 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,14 @@ httpx==0.28.1
# via
# miru-device-sdk
# respx
idna==3.11
idna==3.20
# via
# anyio
# httpx
importlib-metadata==8.7.1
iniconfig==2.1.0 ; python_full_version < '3.10'
iniconfig==2.3.0
# via pytest
iniconfig==2.3.0 ; python_full_version >= '3.10'
# via pytest
markdown-it-py==3.0.0 ; python_full_version < '3.10'
# via rich
markdown-it-py==4.0.0 ; python_full_version >= '3.10'
markdown-it-py==4.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
Expand All @@ -67,28 +63,18 @@ pygments==2.19.2
# pytest
# rich
pyright==1.1.399
pytest==8.4.2 ; python_full_version < '3.10'
# via
# pytest-asyncio
# pytest-xdist
pytest==9.0.2 ; python_full_version >= '3.10'
pytest==9.1.1
# via
# pytest-asyncio
# pytest-xdist
pytest-asyncio==1.2.0 ; python_full_version < '3.10'
pytest-asyncio==1.3.0 ; python_full_version >= '3.10'
pytest-asyncio==1.3.0
pytest-xdist==3.8.0
python-dateutil==2.9.0.post0 ; python_full_version < '3.10'
# via time-machine
respx==0.22.0
rich==14.2.0
ruff==0.14.13
six==1.17.0 ; python_full_version < '3.10'
# via python-dateutil
sniffio==1.3.1
# via miru-device-sdk
time-machine==2.19.0 ; python_full_version < '3.10'
time-machine==3.2.0 ; python_full_version >= '3.10'
time-machine==3.2.0
tomli==2.4.0 ; python_full_version < '3.11'
# via
# mypy
Expand Down
2 changes: 1 addition & 1 deletion scripts/lint
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ else
fi

echo "==> Running pyright"
uv run pyright
uv run pyright -p .

echo "==> Running mypy"
uv run mypy .
Expand Down
2 changes: 1 addition & 1 deletion scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export DEFER_PYDANTIC_BUILD=false

# Note that we need to specify the patch version here so that uv
# won't use unstable (alpha, beta, rc) releases for the tests
PY_VERSION_MIN=">=3.9.0"
PY_VERSION_MIN=">=3.10.0"
PY_VERSION_MAX=">=3.14.0"

function run_tests() {
Expand Down
2 changes: 1 addition & 1 deletion scripts/utils/upload-artifact.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ UPLOAD_RESPONSE=$(curl -v -X PUT \

if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/miru-device-python/$SHA/$FILENAME'\033[0m"
echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/python-device-sdk-staging/$SHA/$FILENAME'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
Expand Down
4 changes: 0 additions & 4 deletions src/miru_device/lib/.keep

This file was deleted.

Loading
Loading