Metadata-Version: 2.5
Name: linc-device-kit
Version: 1.0.0
Summary: Shared device models and protocols for LINC tools
Project-URL: Homepage, https://github.com/LincForge/device-kit
Project-URL: Issues, https://github.com/LincForge/device-kit/issues
Author: LINC Innovations
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Description-Content-Type: text/markdown

# linc-device-kit

Plain data types for tools that drive Android and iOS devices: an accessibility-tree node, a
screen snapshot, device identity, and the `IDeviceIO` protocol a device adapter implements.
Pure dataclasses with no I/O and no dependencies, so an MCP server, a test harness and a
quality checker can share one vocabulary and one compact JSON shape.
[nerve](https://github.com/LincForge/nerve) uses it.

## Install

```bash
pip install linc-device-kit        # or: uv add linc-device-kit
```

Python 3.12+. No runtime dependencies.

## Example

```python
import json

from linc_device_kit import (
    DeviceInfo, DeviceType, ElementNode, Platform, Rect, ScreenState, SemanticRole,
)

login = ElementNode(
    id="login", text="Sign in", role=SemanticRole.BUTTON,
    bounds=Rect(x=40, y=900, width=400, height=96), clickable=True,
)
root = ElementNode(role=SemanticRole.CONTAINER, children=[login])
state = ScreenState(
    device=DeviceInfo("emulator-5554", Platform.ANDROID, DeviceType.EMULATOR),
    timestamp="2026-10-01T12:00:00Z",
    elements=root,
    foreground_app="com.example.app",
)

payload = state.to_dict()  # compact: fields at their defaults are omitted
print(json.dumps(payload["elements"]))
print(payload["element_count"])
assert ScreenState.from_dict(payload) == state
```

```console
$ python example.py
{"role": "container", "children": [{"id": "login", "text": "Sign in", "role": "button", "bounds": {"x": 40, "y": 900, "width": 400, "height": 96}, "clickable": true}]}
2
```

`ElementNode.to_dict()` leaves out every field that is at its default, which keeps an
accessibility tree small enough to hand to an AI agent. `from_dict` restores the defaults, so
`from_dict(to_dict(x)) == x`. `ScreenState.to_dict()` never includes the screenshot bytes.

## What is in it

| Type | What it holds |
|------|---------------|
| `ElementNode` | One accessibility-tree node: id, text, description, value, role, bounds, clickable/enabled/focused/scrollable/checked, children |
| `ScreenState` | A screen at a point in time: device, ISO 8601 timestamp, element tree, optional screenshot bytes, foreground app, screen name; `element_count` is computed from the tree |
| `DeviceInfo` | Serial, platform, device type, model, OS version |
| `Rect` | Axis-aligned bounds: x, y, width, height |
| `Platform`, `DeviceType`, `SemanticRole` | `StrEnum`s, so they serialise as plain strings |
| `IDeviceIO` | `@runtime_checkable` protocol: screenshot, elements, foreground_app, logs, tap, swipe, type_text, press_key, install, launch, kill, clear_app_data, grant_permission, forward_debug_port, list_debug_targets, capabilities |
| `DeviceKitError` | Base of the exception hierarchy (`DeviceNotReachable`, `DeviceNotConfigured`, `InstallFailed`, `BuildFailed`, `SigningError`, `WDAStartupFailed`, `QualityGateFailed`) |

## Development

```bash
uv sync --all-extras
uv run pytest
```

## How this repo works

This is a release mirror of a private LINC repository. The code is built by LINC's AI software
factory under human review, and each release lands here as one commit. Issues are welcome; see
[CONTRIBUTING.md](CONTRIBUTING.md). To report a security problem, see [SECURITY.md](SECURITY.md).

## License

Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).

---

Built by [LINC Innovations](https://lincinnovations.com/audit?utm_source=github&utm_medium=readme&utm_campaign=device-kit). We diagnose flaky device test suites.
