diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..eb9b82eb7de30fb0baefb6c7229c5af99aeff234
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,37 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+*.egg-info/
+dist/
+build/
+
+# Virtual environments (but not in backend/)
+**/.venv/
+**/venv/
+**/ENV/
+**/env/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Git
+.git/
+.gitignore
+
+# Tests
+tests/
+*.egg-info/
+
+# Lock files
+uv.lock
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4ea72a911af2d782f20b992e808276bb838718d8
--- /dev/null
+++ b/.idea/copilot.data.migration.agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7ef04e2ea0752989be4a246d998b53adf46bab42
--- /dev/null
+++ b/.idea/copilot.data.migration.ask.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1f2ea11e7f0069b02b6f715f9735b8a31b234538
--- /dev/null
+++ b/.idea/copilot.data.migration.ask2agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8648f9401aa827dc53e658ac3b1c1f1556df236b
--- /dev/null
+++ b/.idea/copilot.data.migration.edit.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..03d9549ea8e4ada36fb3ecbc30fef08175b7d728
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..90dee705c2feea55b5758429d0dce56882074a46
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+ {}
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3befe1452c370485afd12a903575fd6fe8e07993
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/rmscript_space_demo.iml b/.idea/rmscript_space_demo.iml
new file mode 100644
index 0000000000000000000000000000000000000000..24643cc37449b4bde54411a80b8ed61258225e34
--- /dev/null
+++ b/.idea/rmscript_space_demo.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7df0f812fa08018ba7b2c8849fb28c2b91872677
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..c171e5d4cb6334829b85486c04d6b70b76742df8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+# Dockerfile for RMScript Web Demo on HuggingFace Spaces
+FROM python:3.11-slim
+
+# Set working directory
+WORKDIR /app
+
+# Install system dependencies
+RUN apt-get update && apt-get install -y \
+ build-essential \
+ && rm -rf /var/lib/apt/lists/*
+
+# Copy reachy_mini package source (dependency of rmscript)
+COPY reachy_mini/ /app/reachy_mini/
+
+# Install reachy_mini package
+RUN pip install --no-cache-dir /app/reachy_mini
+
+# Copy rmscript package source
+COPY rmscript/ /app/rmscript/
+
+# Install rmscript package with dependencies
+RUN pip install --no-cache-dir /app/rmscript[scipy]
+
+# Copy backend code
+COPY backend/ /app/backend/
+
+# Install backend dependencies
+WORKDIR /app/backend
+RUN pip install --no-cache-dir fastapi uvicorn[standard] pydantic
+
+# Copy frontend code
+COPY frontend/ /app/frontend/
+
+# Expose port 7860 (required by HuggingFace Spaces)
+EXPOSE 7860
+
+# Run the FastAPI server
+CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b75ad2728a03cf19942c0f80d9e2bfe847f0641
--- /dev/null
+++ b/README.md
@@ -0,0 +1,111 @@
+---
+title: RMScript Web Demo
+emoji: 🤖
+colorFrom: purple
+colorTo: blue
+sdk: docker
+app_port: 7860
+pinned: false
+---
+
+# RMScript Web Demo
+
+Web-based IDE for writing, verifying, and executing rmscript code on Reachy Mini.
+
+## Architecture
+
+- **Frontend** (`frontend/`): HTML/CSS/JavaScript editor with live compilation and robot control
+- **Backend** (`backend/`): FastAPI server for rmscript compilation and validation
+- **Robot**: Reachy Mini robot daemon running on localhost:8000
+
+## Quick Start
+
+### On HuggingFace Spaces
+
+Just open the Space - the web IDE will load automatically.
+
+**Requirements:**
+- Reachy Mini daemon running on `localhost:8000` on your local machine
+- Modern web browser with WebSocket support
+
+### Local Development
+
+1. Install rmscript package:
+```bash
+cd rmscript
+uv sync
+```
+
+2. Start backend server:
+```bash
+cd backend
+uv sync
+uv run python app.py
+```
+
+3. Make sure your Reachy Mini daemon is running on `localhost:8000`
+
+4. Open `frontend/index.html` in your web browser
+
+## Features
+
+- **Code Editor**: Write rmscript with syntax examples
+- **Verify Button**: Check script validity without executing
+- **Execute Button**: Compile and run script on connected robot
+- **Live Compilation**: See IR (Intermediate Representation) of your script
+- **Example Scripts**: Load pre-written examples (basic, complex, repeat)
+- **Real-time Logging**: Console shows compilation errors, warnings, and execution progress
+
+## Usage
+
+1. Write rmscript in the editor (or load an example)
+2. Click "Verify" to check syntax and semantics
+3. Review any errors/warnings in the console
+4. Click "Execute" to run on the robot (robot must be connected)
+5. Watch execution progress in the Execution Info panel
+
+## Current Limitations
+
+- Sound and picture actions are not yet implemented in execution
+
+## Deployment to HuggingFace Spaces
+
+To deploy this demo to HuggingFace Spaces:
+
+1. **Prepare the repository structure:**
+ - Copy the `rmscript` and `reachy_mini` directories into the `rmscript_space_demo` directory:
+ ```bash
+ cd rmscript_space_demo
+ cp -r ../rmscript ./rmscript
+ cp -r ../reachy_mini ./reachy_mini
+ ```
+ - Your structure should be:
+ ```
+ rmscript_space_demo/
+ ├── Dockerfile
+ ├── README.md
+ ├── .dockerignore
+ ├── backend/
+ ├── frontend/
+ ├── rmscript/ ← copied source
+ └── reachy_mini/ ← copied source (dependency)
+ ```
+
+2. **Create a new Space:**
+ - Go to https://huggingface.co/new-space
+ - Select "Docker" as the SDK
+ - Push this directory to your Space repository
+
+3. **The Space will:**
+ - Build the Docker container
+ - Install rmscript and dependencies
+ - Serve the web IDE on port 7860
+
+**Note:** The Space connects to your local robot daemon on `localhost:8000`. Make sure it's running when using the demo.
+
+## Development
+
+To modify the demo:
+- Frontend code: `frontend/app.js`
+- Backend API: `backend/app.py`
+- Styling: `frontend/index.html` (embedded CSS)
diff --git a/backend/README.md b/backend/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6b9682573f7dc7cb0c62ee473b277d0327907822
--- /dev/null
+++ b/backend/README.md
@@ -0,0 +1,44 @@
+# RMScript Web Demo - Backend
+
+FastAPI backend for compiling and validating rmscript code.
+
+## Installation
+
+```bash
+# Install dependencies with uv
+uv sync
+```
+
+## Running
+
+```bash
+uv run python app.py
+```
+
+Or with uvicorn directly:
+
+```bash
+uv run uvicorn app:app --reload --port 8001
+```
+
+The API will be available at http://localhost:8001
+
+## API Endpoints
+
+- `GET /` - API information
+- `POST /api/verify` - Verify rmscript syntax (returns errors/warnings)
+- `POST /api/compile` - Compile rmscript to IR (returns IR actions)
+
+## Example Usage
+
+```bash
+# Verify a script
+curl -X POST http://localhost:8001/api/verify \
+ -H "Content-Type: application/json" \
+ -d '{"source": "look left\nwait 1s\nlook right"}'
+
+# Compile a script
+curl -X POST http://localhost:8001/api/compile \
+ -H "Content-Type: application/json" \
+ -d '{"source": "look left\nwait 1s\nlook right"}'
+```
diff --git a/backend/app.py b/backend/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ccb6b67f06f4a312926b9afed7a6723fcb4ecc7
--- /dev/null
+++ b/backend/app.py
@@ -0,0 +1,252 @@
+"""FastAPI backend for rmscript web demo."""
+
+import logging
+from pathlib import Path
+from typing import Any, Dict, List
+from fastapi import FastAPI, HTTPException
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import FileResponse
+from fastapi.staticfiles import StaticFiles
+from pydantic import BaseModel
+
+from rmscript import compile_script
+
+# Configure logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+# Create FastAPI app
+app = FastAPI(
+ title="RMScript Web Demo API",
+ description="Backend API for compiling and validating rmscript code",
+ version="1.0.0"
+)
+
+# Enable CORS for local development
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # In production, specify exact origins
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# Set up paths for serving frontend
+BACKEND_DIR = Path(__file__).parent
+FRONTEND_DIR = BACKEND_DIR.parent / "frontend"
+
+# Mount static files (CSS, JS)
+if FRONTEND_DIR.exists():
+ app.mount("/static", StaticFiles(directory=str(FRONTEND_DIR)), name="static")
+
+
+class ScriptInput(BaseModel):
+ """Input model for script compilation."""
+
+ source: str
+
+
+class CompilationError(BaseModel):
+ """Compilation error model."""
+
+ line: int
+ column: int
+ message: str
+ severity: str
+
+
+class VerifyResponse(BaseModel):
+ """Response model for script verification."""
+
+ success: bool
+ errors: List[Dict[str, Any]]
+ warnings: List[Dict[str, Any]]
+ name: str = ""
+ description: str = ""
+
+
+class IRActionModel(BaseModel):
+ """Simplified IR action model for JSON serialization."""
+
+ type: str # "action", "wait", "picture", "sound"
+ duration: float = 0.0
+ # For movement actions
+ head_pose: List[List[float]] | None = None # 4x4 matrix as nested list
+ antennas: List[float] | None = None # [left, right] in radians
+ body_yaw: float | None = None # radians
+ # For sound actions
+ sound_name: str | None = None
+ blocking: bool = False
+ loop: bool = False
+ # Metadata
+ source_line: int = 0
+
+
+class CompileResponse(BaseModel):
+ """Response model for script compilation."""
+
+ success: bool
+ errors: List[Dict[str, Any]]
+ warnings: List[Dict[str, Any]]
+ name: str = ""
+ description: str = ""
+ ir: List[IRActionModel]
+
+
+@app.get("/")
+async def root():
+ """Serve the frontend application."""
+ index_path = FRONTEND_DIR / "index.html"
+ if index_path.exists():
+ return FileResponse(index_path)
+ return {
+ "name": "RMScript Web Demo API",
+ "version": "1.0.0",
+ "endpoints": {
+ "/api/verify": "POST - Verify rmscript syntax and semantics",
+ "/api/compile": "POST - Compile rmscript to IR",
+ }
+ }
+
+
+@app.post("/api/verify", response_model=VerifyResponse)
+async def verify_script(input: ScriptInput):
+ """Verify rmscript without generating IR.
+
+ Args:
+ input: Script source code
+
+ Returns:
+ Verification result with errors and warnings
+
+ """
+ logger.info(f"Verifying script ({len(input.source)} chars)")
+
+ try:
+ result = compile_script(input.source)
+
+ # Convert errors and warnings to dict format
+ errors = [
+ {
+ "line": e.line,
+ "column": e.column,
+ "message": e.message,
+ "severity": e.severity
+ }
+ for e in result.errors
+ ]
+
+ warnings = [
+ {
+ "line": w.line,
+ "column": w.column,
+ "message": w.message,
+ "severity": w.severity
+ }
+ for w in result.warnings
+ ]
+
+ return VerifyResponse(
+ success=result.success,
+ errors=errors,
+ warnings=warnings,
+ name=result.name,
+ description=result.description
+ )
+
+ except Exception as e:
+ logger.error(f"Verification error: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/api/compile", response_model=CompileResponse)
+async def compile_rmscript(input: ScriptInput):
+ """Compile rmscript to intermediate representation.
+
+ Args:
+ input: Script source code
+
+ Returns:
+ Compilation result with IR
+
+ """
+ logger.info(f"Compiling script ({len(input.source)} chars)")
+
+ try:
+ from rmscript.ir import IRAction, IRWaitAction, IRPictureAction, IRPlaySoundAction
+
+ result = compile_script(input.source)
+
+ # Convert errors and warnings to dict format
+ errors = [
+ {
+ "line": e.line,
+ "column": e.column,
+ "message": e.message,
+ "severity": e.severity
+ }
+ for e in result.errors
+ ]
+
+ warnings = [
+ {
+ "line": w.line,
+ "column": w.column,
+ "message": w.message,
+ "severity": w.severity
+ }
+ for w in result.warnings
+ ]
+
+ # Convert IR to JSON-serializable format
+ ir_json = []
+ for action in result.ir:
+ if isinstance(action, IRAction):
+ ir_json.append(IRActionModel(
+ type="action",
+ duration=action.duration,
+ head_pose=action.head_pose.tolist() if action.head_pose is not None else None,
+ antennas=action.antennas if action.antennas is not None else None,
+ body_yaw=action.body_yaw,
+ source_line=action.source_line
+ ))
+ elif isinstance(action, IRWaitAction):
+ ir_json.append(IRActionModel(
+ type="wait",
+ duration=action.duration,
+ source_line=action.source_line
+ ))
+ elif isinstance(action, IRPictureAction):
+ ir_json.append(IRActionModel(
+ type="picture",
+ source_line=action.source_line
+ ))
+ elif isinstance(action, IRPlaySoundAction):
+ ir_json.append(IRActionModel(
+ type="sound",
+ duration=action.duration or 0.0,
+ sound_name=action.sound_name,
+ blocking=action.blocking,
+ loop=action.loop,
+ source_line=action.source_line
+ ))
+
+ logger.info(f"Compiled {len(ir_json)} IR actions")
+
+ return CompileResponse(
+ success=result.success,
+ errors=errors,
+ warnings=warnings,
+ name=result.name,
+ description=result.description,
+ ir=ir_json
+ )
+
+ except Exception as e:
+ logger.error(f"Compilation error: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+if __name__ == "__main__":
+ import uvicorn
+ uvicorn.run(app, host="0.0.0.0", port=8001, log_level="info")
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..b16eab1eb4576455b454da1becacfdee577030f3
--- /dev/null
+++ b/backend/pyproject.toml
@@ -0,0 +1,14 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "rmscript-web-backend"
+version = "1.0.0"
+requires-python = ">=3.11"
+dependencies = [
+ "fastapi>=0.104.0",
+ "uvicorn[standard]>=0.24.0",
+ "pydantic>=2.4.0",
+ "rmscript[reachy,scipy] @ file:///Users/david/projects/reachy-mini/rmscript",
+]
diff --git a/backend/requirements.txt b/backend/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a1fe4848660d4b5de68d82aa10f3dc2ec23424a5
--- /dev/null
+++ b/backend/requirements.txt
@@ -0,0 +1,4 @@
+fastapi>=0.104.0
+uvicorn[standard]>=0.24.0
+pydantic>=2.4.0
+rmscript>=0.1.0
diff --git a/backend/uv.lock b/backend/uv.lock
new file mode 100644
index 0000000000000000000000000000000000000000..dc53d317594e898beb50af2a0891468419c91fd8
--- /dev/null
+++ b/backend/uv.lock
@@ -0,0 +1,2108 @@
+version = 1
+revision = 3
+requires-python = ">=3.11"
+resolution-markers = [
+ "python_full_version >= '3.12' and sys_platform == 'darwin'",
+ "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" },
+ { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" },
+ { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" },
+ { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" },
+ { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" },
+ { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" },
+ { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" },
+ { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" },
+ { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" },
+ { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" },
+ { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" },
+ { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" },
+ { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" },
+ { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" },
+ { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" },
+ { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" },
+ { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" },
+ { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" },
+ { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" },
+ { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" },
+ { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" },
+ { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" },
+ { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" },
+ { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" },
+ { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" },
+ { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" },
+ { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
+]
+
+[[package]]
+name = "annotated-doc"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" },
+]
+
+[[package]]
+name = "asgiref"
+version = "3.11.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/b9/4db2509eabd14b4a8c71d1b24c8d5734c52b8560a7b1e1a8b56c8d25568b/asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4", size = 37969, upload-time = "2025-11-19T15:32:20.106Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096, upload-time = "2025-11-19T15:32:19.004Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "25.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.11.12"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "cv2-enumerate-cameras"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/cf/a6eb5fa54d7dfc87c93a6161aef57527c6ef4d8239be7cef10d13fb9efd6/cv2_enumerate_cameras-1.3.0.tar.gz", hash = "sha256:795d8005b0e92ca117e1b8c4849c752edb39b7e2a1818b2d7ac4463aa26caac1", size = 12455, upload-time = "2025-09-12T04:42:33.549Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/38/cc570b8579a2be52d0a3f6dba7afad536bc6d41cfc44108727ef7d8b185a/cv2_enumerate_cameras-1.3.0-cp32-abi3-win32.whl", hash = "sha256:bd24be3c2df42b88da59e9f6029b97484fc8468678a2be3900aa717835c40f6a", size = 20406, upload-time = "2025-09-12T04:42:28.463Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/ef/0a6e095462e90d12358ef68653c7ea4d86dd6f5dd8f433279ecb5745e9ca/cv2_enumerate_cameras-1.3.0-cp32-abi3-win_amd64.whl", hash = "sha256:ef892cc7d92dec3ad0f696fa592a9d5461e593698048f84e877353cffcfc486f", size = 21296, upload-time = "2025-09-12T04:42:30.87Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/d5/6a6b74c92b6901d7077eb67284010354d90762702ed3451ea5d6297ef692/cv2_enumerate_cameras-1.3.0-py3-none-any.whl", hash = "sha256:7a67d8cc98fd112c3df51ff699c6953884e96088ced2ee05191bdeaaa2e1e847", size = 11571, upload-time = "2025-09-12T04:42:32.272Z" },
+]
+
+[[package]]
+name = "eclipse-zenoh"
+version = "1.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/3b/15bb190da421536b28de45438ad328925f203b5b274c5e92a7dcc6f7ac14/eclipse_zenoh-1.6.2.tar.gz", hash = "sha256:852b649a9d1c482edb690759311d5322fa10e5388ba6dec507d35151b48ceb35", size = 132916, upload-time = "2025-10-17T20:11:01.322Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/13/0e6511e1f007686084beff96d960407edc8303a0aac267689087e1be3e11/eclipse_zenoh-1.6.2-cp39-abi3-linux_armv6l.whl", hash = "sha256:b311c5e4626cc2faad367ef0edbb09079dafefc004531f135cafd22af48f362e", size = 9454073, upload-time = "2025-10-17T20:10:46.806Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/84/f36e224fb7eb05927e55ea7cd2ee000f7a9254d194f7ab539627a1635dd3/eclipse_zenoh-1.6.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:13d0b10c09fbd4233356a38b11cf008112acefda308a4dd4feb3c8e74c674065", size = 17565641, upload-time = "2025-10-17T20:10:49.061Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/7d/98194e5f4f369eb7f49024d7d4536342636bee216f191cbb111c7ee86764/eclipse_zenoh-1.6.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:29f081cf6ba5eee7c72606cab1e534e6e6a825c08899529c176d7a6fa1cff6c4", size = 8984387, upload-time = "2025-10-17T20:10:50.736Z" },
+ { url = "https://files.pythonhosted.org/packages/04/17/0b550ec08095471bd099258ea23685f569dba79394fa22dd25c232d75cae/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c1c332334ec4bceb0856472146684cfb4230e55c0409cba6b369e34c87a5f5cb", size = 9337753, upload-time = "2025-10-17T20:10:52.696Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/bc/9c2196cc742ad0ccdcc9e942bea3d627f70e5a17708ab412e05bbca07180/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cf678ed2578b954cf8d1863c897c9d4404d9bac1c9560dfdb9a300bb488e01", size = 10147324, upload-time = "2025-10-17T20:10:55.234Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/81/7ac634430be1fd15aea65d6e849f7d73d0a8cf285dd74e8e39cd7b7ea2e2/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5db633632e3da9c63ae70e219f2a84dca3dcb08125fb9aef9f11602104790afb", size = 9626850, upload-time = "2025-10-17T20:10:56.785Z" },
+ { url = "https://files.pythonhosted.org/packages/29/fe/c5b2e65e0c2800b4e1a02b388e0ddf7c3b71f478757bb6a294c021d49e84/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e7f9bf3849567631caa3bdeecf2edce66461b1ae511f28dfafea6089022d02e1", size = 9487136, upload-time = "2025-10-17T20:10:58.359Z" },
+ { url = "https://files.pythonhosted.org/packages/77/93/4df169108c4313d0f476bb9abd7d76496059e8bfcc3a98168aa26cc73b00/eclipse_zenoh-1.6.2-cp39-abi3-win_amd64.whl", hash = "sha256:f59f75f0d733daf5059843037e63bb62dd5268809d041a150bcfd8f8d62b9b08", size = 7805746, upload-time = "2025-10-17T20:11:00.257Z" },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.122.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b2/de/3ee97a4f6ffef1fb70bf20561e4f88531633bb5045dc6cebc0f8471f764d/fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b", size = 346436, upload-time = "2025-11-24T19:17:47.95Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/93/aa8072af4ff37b795f6bbf43dcaf61115f40f49935c7dbb180c9afc3f421/fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67", size = 110671, upload-time = "2025-11-24T19:17:45.96Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" },
+ { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" },
+ { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" },
+ { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" },
+ { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" },
+ { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" },
+ { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" },
+ { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" },
+ { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" },
+ { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" },
+ { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" },
+ { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" },
+ { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" },
+ { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" },
+ { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" },
+ { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
+]
+
+[[package]]
+name = "fsspec"
+version = "2025.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "hf-xet"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" },
+ { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" },
+ { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" },
+ { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" },
+ { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" },
+ { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" },
+ { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" },
+]
+
+[[package]]
+name = "httptools"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" },
+ { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" },
+ { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" },
+ { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" },
+ { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" },
+ { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" },
+ { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" },
+ { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" },
+ { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.34.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "libusb-package"
+version = "1.0.26.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "importlib-resources" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/bf/3fe9d322e2dcd0437ae2bd6a039117965702ed473ca59d2d6a1c39838009/libusb_package-1.0.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:60e15d7d3e4aab31794da95641bc28c4ffec9e24f50891ce33f75794b8f531f3", size = 63864, upload-time = "2025-04-01T12:59:14.567Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/70/df0348c11e6aaead4a66cc59840e102ddf64baf8e4b2c1ad5cff1ca83554/libusb_package-1.0.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d93a6137609cf72dc5db69bc337ddf96520231e395beeff69fa77a923090003", size = 59502, upload-time = "2025-04-01T12:59:15.863Z" },
+ { url = "https://files.pythonhosted.org/packages/27/67/6c84eebc9fcdf7f26704b5d32b51b3ee5bf4e9090d61286941257bdc8702/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fafb69c5fd42b241fbd20493d014328c507d34e1b7ceb883a20ef14565b26898", size = 70247, upload-time = "2025-04-01T14:53:07.606Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/b4/cbcc42ca4b3d8778bf081b96e6e6288a437d82a4cc4e9b982bef40a88856/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c206cd8a30565a0cede3ba426929e70a37e7b769e41a5ac7f00ca6737dc5d", size = 74537, upload-time = "2025-04-01T14:53:08.61Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/0f/fd203fb1fa5eda1d446f345d84205f23533767e6ef837a7c77a2599d5783/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a2041331c087d5887969405837f86c8422120fe9ba3e6faa44bf4810f07b71", size = 70653, upload-time = "2025-04-01T14:53:09.576Z" },
+ { url = "https://files.pythonhosted.org/packages/79/ef/dcc682cb4b29c4d4cdb23df65825c6276753184f6a7b4338c54a59a54c20/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48b536a1279ee0dbf70b898cffd16cd661774d2c8bbec8ff7178a5bc20196af3", size = 71859, upload-time = "2025-04-01T14:53:10.987Z" },
+ { url = "https://files.pythonhosted.org/packages/62/4d/323d5ac4ba339362e4b148c291fbc6e7ee04c6395d5fec967b32432db5c5/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f273e33ff1810242f81ea3a0286e25887d99d99019ba83e08be0d1ca456cc05", size = 76476, upload-time = "2025-04-01T14:53:12.019Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3b/506db7f6cbe5dc2f38c14b272b8faf4d43e5559ac99d4dce1a41026ec925/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67476093601e1ea58a6130426795b906acd8d18d51c84e29a3a69548a5dfcf5d", size = 71037, upload-time = "2025-04-01T14:53:13.42Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/40/2538763c06e07bbbe0a5c8830779ef1ed1cea845264a91973bf31b9ecce5/libusb_package-1.0.26.3-cp311-cp311-win32.whl", hash = "sha256:8f3eed2852ee4f08847a221749a98d0f4f3962f8bed967e2253327db1171ba60", size = 77642, upload-time = "2025-04-01T12:58:03.106Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/46/0cd5ea91c5bbe6293c0936c96915051e31750f72e9556718af666af3fe45/libusb_package-1.0.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:b48b5f5b17c7ac5e315e233f9ee801f730aac6183eb53a3226b01245d7bcfe00", size = 90592, upload-time = "2025-04-01T12:58:04.103Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/f6/83e13936b5799360eae8f0e31b5b298dd092451b91136d7cd13852777954/libusb_package-1.0.26.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c9404298485762a4e73b416e8a3208d33aa3274fb9b870c2a1cacba7e2918f19", size = 62045, upload-time = "2025-04-01T12:59:16.817Z" },
+ { url = "https://files.pythonhosted.org/packages/33/97/86ed73880b6734c9383be5f34061b541e8fe5bd0303580b1f5abe2962d58/libusb_package-1.0.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8126f6711318dad4cb2805ea20cd47b895a847207087d8fdb032e082dd7a2e24", size = 59502, upload-time = "2025-04-01T12:59:17.72Z" },
+ { url = "https://files.pythonhosted.org/packages/95/f7/27b67b8fe63450abf0b0b66aacf75d5d64cdf30317e214409ceb534f34b4/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11c219366e4a2368117b9a9807261f3506b5623531f8b8ce41af5bbaec8156a0", size = 70247, upload-time = "2025-04-01T14:53:14.387Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/11/613543f9c6dab5a82eefd0c78d52d08b5d9eb93a0362151fbedf74b32541/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8809a50d8ab84297344c54e862027090c0d73b14abef843a8b5f783313f49457", size = 74537, upload-time = "2025-04-01T14:53:15.345Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/43/5a2331615693b56221a902869fb2094d9a0b9a764a8706c8ba16e915f77c/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a83067c3dfdbb3856badb4532eaea22e8502b52ce4245f5ab46acf93d7fbd471", size = 70652, upload-time = "2025-04-01T14:53:16.319Z" },
+ { url = "https://files.pythonhosted.org/packages/44/1a/186d4ec86421b69feb45e214edb5301fbcb9e8dc9df963678aeff1a447d5/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b56be087ea9cde8e50fb02740a4f0cefb6f63c61ac2e7812a9244487614a3973", size = 71860, upload-time = "2025-04-01T14:53:17.87Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/3c/8cebdad822d7bfcb683a77d5fd113fbc6f72516cfb7c1c3a274fefafa8e9/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea0f6bf40e54b1671e763e40c9dbed46bf7f596a4cd98b7c827e147f176d8c97", size = 76476, upload-time = "2025-04-01T14:53:19.202Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5f/30c625b6c4ecd14871644c1d16e97d7c971f82a0f87a9cfa81022f85bcfc/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b40f77df991c6db8621de9575504886eca03a00277e521a4d64b66cbef8f6997", size = 71037, upload-time = "2025-04-01T14:53:21.359Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/e9/3aa3ff3242867b7f22ee3ce28d0e93ff88547f170ca1b8a6edc59660d974/libusb_package-1.0.26.3-cp312-cp312-win32.whl", hash = "sha256:6eee99c9fde137443869c8604d0c01b2127a9545ebc59d06a3376cf1d891e786", size = 77642, upload-time = "2025-04-01T12:58:05.471Z" },
+ { url = "https://files.pythonhosted.org/packages/15/0e/913ddb1849f828fc385438874c34541939d9b06c0e5616f48f24cddd24de/libusb_package-1.0.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:5e09c0b6b3cd475841cffe78e46e91df58f0c6c02ea105ea1a4d0755a07c8006", size = 90593, upload-time = "2025-04-01T12:58:06.798Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/b8/23bc7f3f53b4a5b1027c721ec3eb42324ca1ec56355f0d0851307adc7c6c/libusb_package-1.0.26.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:04c4505e2ca68d3dc6938f116ff9bf82daffb06c1a97aba08293a84715a998da", size = 62045, upload-time = "2025-04-01T12:59:18.698Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/f8/e3be96d0604070488ddc5ce5af1976992e1f4a00e6441c94edf807f274d5/libusb_package-1.0.26.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4961cdb3c622aa9f858d3e4f99a58ce5e822a97c22abc77040fd806cb5fa4c66", size = 59502, upload-time = "2025-04-01T12:59:19.632Z" },
+ { url = "https://files.pythonhosted.org/packages/24/d5/df1508df5e6776ac8a09a2858991df29bc96ea6a0d1f90240b1c4d59b45d/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16182670e0c23235521b447054c5a01600bd8f1eed3bb08eedbb0d9f8a43249f", size = 70247, upload-time = "2025-04-01T14:53:22.328Z" },
+ { url = "https://files.pythonhosted.org/packages/65/01/4cc9eed12b9214c088cfa8055ece3b1db970404400be9d7e3dda68d198f2/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75ea57b2cc903d28ec1d4b909902df442cbf21949d80d5b3d8b9dac36ac45d1a", size = 74537, upload-time = "2025-04-01T14:53:23.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/83/9eb317f706f588f4b6679bddb8abee3b115ce53dc3fa560cca59910f8807/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d30b51b128ef5112fff73268b4696fea00b5676b3f39a5ee859bd76cb3ace5", size = 70651, upload-time = "2025-04-01T14:53:24.33Z" },
+ { url = "https://files.pythonhosted.org/packages/22/49/85d3b307b4a20cf0150ab381e6e0385e5b78cb5dede8bade0a2d655d3fd3/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c098dcfcfa8000cab42f33e19628c8fdb16111670db381048b2993651f2413b", size = 71860, upload-time = "2025-04-01T14:53:25.752Z" },
+ { url = "https://files.pythonhosted.org/packages/da/7a/2271a5ae542d9036d9254415ae745d5c5d01a08d56d13054b2439bf9d392/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:93169aeab0657255fe6c9f757cf408f559db13827a1d122fc89239994d7d51f1", size = 76477, upload-time = "2025-04-01T14:53:27.564Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9d/d06d53994bb164564ec142ef631a4afa31e324994cf223f169ecca127f3a/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:63257653ee1ee06aa836e942f4bb89a1d7a0c6ae3d6183647a9011e585ffa1e3", size = 71036, upload-time = "2025-04-01T14:53:29.011Z" },
+ { url = "https://files.pythonhosted.org/packages/32/3d/97f775a1d582548b1eb2a42444c58813e5fd93d568fc3b9ace59f64df527/libusb_package-1.0.26.3-cp313-cp313-win32.whl", hash = "sha256:05db4cc801db2e6373a808725748a701509f9450fecf393fbebab61c45d50b50", size = 77642, upload-time = "2025-04-01T12:58:07.774Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c4/d5234607697ca60593fbef88428a154317ac31f5c58ee23337b8a9360e91/libusb_package-1.0.26.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cd4aec825dac2b4fa5d23b37f6d72e63a1127987e5a073dabeb7b73528623a3", size = 90593, upload-time = "2025-04-01T12:58:08.676Z" },
+]
+
+[[package]]
+name = "log-throttling"
+version = "0.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/be/09fda383d82a7b91f12ff5002bc909e101769c802ff3b1d2cb9f533d1a2f/log-throttling-0.0.3.tar.gz", hash = "sha256:df714e1448cd9cb4c916cf668ab88323032e61ece733548708d996ecf63190b9", size = 3533, upload-time = "2022-03-08T16:29:23.674Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/6e/995ebad4ab08250b4afe1d91fe3086b863ed9bb74539d90485250a50f08a/log_throttling-0.0.3-py3-none-any.whl", hash = "sha256:a82508f489dd44a60b737a0339c784dd8ee2b9db16bdcf587a8ae1012a7f6ffc", size = 4081, upload-time = "2022-03-08T16:29:21.443Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
+ { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "6.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" },
+ { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" },
+ { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" },
+ { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" },
+ { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" },
+ { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" },
+ { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" },
+ { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" },
+ { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" },
+ { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" },
+ { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" },
+ { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" },
+ { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" },
+ { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" },
+ { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" },
+ { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" },
+ { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" },
+ { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" },
+ { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" },
+ { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" },
+ { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" },
+ { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" },
+ { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" },
+ { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" },
+ { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" },
+ { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" },
+ { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" },
+ { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" },
+ { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" },
+ { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" },
+ { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" },
+ { url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" },
+ { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" },
+ { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" },
+ { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" },
+ { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" },
+ { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" },
+ { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" },
+ { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" },
+ { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" },
+ { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" },
+ { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" },
+ { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" },
+ { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" },
+ { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" },
+ { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" },
+ { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" },
+ { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" },
+ { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" },
+ { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" },
+ { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" },
+ { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" },
+ { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" },
+ { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" },
+ { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" },
+ { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" },
+ { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" },
+ { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" },
+ { url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" },
+ { url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" },
+]
+
+[[package]]
+name = "opencv-python"
+version = "4.11.0.86"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322, upload-time = "2025-01-16T13:52:25.887Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197, upload-time = "2025-01-16T13:55:21.222Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439, upload-time = "2025-01-16T13:51:35.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597, upload-time = "2025-01-16T13:52:08.836Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337, upload-time = "2025-01-16T13:52:13.549Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044, upload-time = "2025-01-16T13:52:21.928Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
+[[package]]
+name = "pip"
+version = "25.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" },
+]
+
+[[package]]
+name = "propcache"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" },
+ { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" },
+ { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" },
+ { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" },
+ { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" },
+ { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" },
+ { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" },
+ { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" },
+ { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" },
+ { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" },
+ { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" },
+ { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" },
+ { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" },
+ { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" },
+ { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" },
+ { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" },
+ { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" },
+ { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" },
+ { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" },
+ { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" },
+ { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" },
+ { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" },
+ { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" },
+ { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" },
+ { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" },
+ { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" },
+ { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
+]
+
+[[package]]
+name = "psutil"
+version = "7.1.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/93/0c49e776b8734fef56ec9c5c57f923922f2cf0497d62e0f419465f28f3d0/psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc", size = 239751, upload-time = "2025-11-02T12:25:58.161Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8d/b31e39c769e70780f007969815195a55c81a63efebdd4dbe9e7a113adb2f/psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0", size = 240368, upload-time = "2025-11-02T12:26:00.491Z" },
+ { url = "https://files.pythonhosted.org/packages/62/61/23fd4acc3c9eebbf6b6c78bcd89e5d020cfde4acf0a9233e9d4e3fa698b4/psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7", size = 287134, upload-time = "2025-11-02T12:26:02.613Z" },
+ { url = "https://files.pythonhosted.org/packages/30/1c/f921a009ea9ceb51aa355cb0cc118f68d354db36eae18174bab63affb3e6/psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251", size = 289904, upload-time = "2025-11-02T12:26:05.207Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/82/62d68066e13e46a5116df187d319d1724b3f437ddd0f958756fc052677f4/psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa", size = 249642, upload-time = "2025-11-02T12:26:07.447Z" },
+ { url = "https://files.pythonhosted.org/packages/df/ad/c1cd5fe965c14a0392112f68362cfceb5230819dbb5b1888950d18a11d9f/psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee", size = 245518, upload-time = "2025-11-02T12:26:09.719Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/bb/6670bded3e3236eb4287c7bcdc167e9fae6e1e9286e437f7111caed2f909/psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353", size = 239843, upload-time = "2025-11-02T12:26:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/66/853d50e75a38c9a7370ddbeefabdd3d3116b9c31ef94dc92c6729bc36bec/psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b", size = 240369, upload-time = "2025-11-02T12:26:14.358Z" },
+ { url = "https://files.pythonhosted.org/packages/41/bd/313aba97cb5bfb26916dc29cf0646cbe4dd6a89ca69e8c6edce654876d39/psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9", size = 288210, upload-time = "2025-11-02T12:26:16.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/fa/76e3c06e760927a0cfb5705eb38164254de34e9bd86db656d4dbaa228b04/psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f", size = 291182, upload-time = "2025-11-02T12:26:18.848Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/1d/5774a91607035ee5078b8fd747686ebec28a962f178712de100d00b78a32/psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7", size = 250466, upload-time = "2025-11-02T12:26:21.183Z" },
+ { url = "https://files.pythonhosted.org/packages/00/ca/e426584bacb43a5cb1ac91fae1937f478cd8fbe5e4ff96574e698a2c77cd/psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264", size = 245756, upload-time = "2025-11-02T12:26:23.148Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" },
+ { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.23"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.12.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.41.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
+]
+
+[[package]]
+name = "pyobjc-core"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/95/df/d2b290708e9da86d6e7a9a2a2022b91915cf2e712a5a82e306cb6ee99792/pyobjc_core-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c918ebca280925e7fcb14c5c43ce12dcb9574a33cccb889be7c8c17f3bcce8b6", size = 671263, upload-time = "2025-11-14T09:31:35.231Z" },
+ { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" },
+ { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" },
+ { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-avfoundation"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cd/42/c026ab308edc2ed5582d8b4b93da6b15d1b6557c0086914a4aabedd1f032/pyobjc_framework_avfoundation-12.1.tar.gz", hash = "sha256:eda0bb60be380f9ba2344600c4231dd58a3efafa99fdc65d3673ecfbb83f6fcb", size = 310047, upload-time = "2025-11-14T10:09:40.069Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/5a/4ef36b309138840ff8cd85364f66c29e27023f291004c335a99f6e87e599/pyobjc_framework_avfoundation-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82cc2c2d9ab6cc04feeb4700ff251d00f1fcafff573c63d4e87168ff80adb926", size = 83328, upload-time = "2025-11-14T09:37:40.808Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/00/ca471e5dd33f040f69320832e45415d00440260bf7f8221a9df4c4662659/pyobjc_framework_avfoundation-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bf634f89265b4d93126153200d885b6de4859ed6b3bc65e69ff75540bc398406", size = 83375, upload-time = "2025-11-14T09:37:47.262Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/d4/ade88067deff45858b457648dd82c9363977eb1915efd257232cd06bdac1/pyobjc_framework_avfoundation-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f8ac7f7e0884ac8f12009cdb9d4fefc2f269294ab2ccfd84520a560859b69cec", size = 83413, upload-time = "2025-11-14T09:37:53.759Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/3a/fa699d748d6351fa0aeca656ea2f9eacc36e31203dfa56bc13c8a3d26d7d/pyobjc_framework_avfoundation-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:51aba2c6816badfb1fb5a2de1b68b33a23f065bf9e3b99d46ede0c8c774ac7a4", size = 83860, upload-time = "2025-11-14T09:38:00.051Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/65/a79cf3b8935a78329ac1107056b91868a581096a90ab6ddff5fd28db4947/pyobjc_framework_avfoundation-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9a3ffd1ae90bd72dbcf2875aa9254369e805b904140362a7338ebf1af54201a6", size = 83629, upload-time = "2025-11-14T09:38:06.697Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/03/4125204a17cd7b4de1fdfc38b280a47d0d8f8691a4ee306ebb41b58ff030/pyobjc_framework_avfoundation-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:394c99876b9a38db4851ddf8146db363556895c12e9c711ccd3c3f907ac8e273", size = 83962, upload-time = "2025-11-14T09:38:13.153Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-cocoa"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/07/5760735c0fffc65107e648eaf7e0991f46da442ac4493501be5380e6d9d4/pyobjc_framework_cocoa-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f52228bcf38da64b77328787967d464e28b981492b33a7675585141e1b0a01e6", size = 383812, upload-time = "2025-11-14T09:40:53.169Z" },
+ { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" },
+ { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" },
+ { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" },
+ { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coreaudio"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/84/d1/0b884c5564ab952ff5daa949128c64815300556019c1bba0cf2ca752a1a0/pyobjc_framework_coreaudio-12.1.tar.gz", hash = "sha256:a9e72925fcc1795430496ce0bffd4ddaa92c22460a10308a7283ade830089fe1", size = 75077, upload-time = "2025-11-14T10:13:22.345Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/25/491ff549fd9a40be4416793d335bff1911d3d1d1e1635e3b0defbd2cf585/pyobjc_framework_coreaudio-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a452de6b509fa4a20160c0410b72330ac871696cd80237883955a5b3a4de8f2a", size = 35327, upload-time = "2025-11-14T09:43:32.523Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/48/05b5192122e23140cf583eac99ccc5bf615591d6ff76483ba986c38ee750/pyobjc_framework_coreaudio-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a5ad6309779663f846ab36fe6c49647e470b7e08473c3e48b4f004017bdb68a4", size = 36908, upload-time = "2025-11-14T09:43:36.108Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/ce/45808618fefc760e2948c363e0a3402ff77690c8934609cd07b19bc5b15f/pyobjc_framework_coreaudio-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3d8ef424850c8ae2146f963afaec6c4f5bf0c2e412871e68fb6ecfb209b8376f", size = 36935, upload-time = "2025-11-14T09:43:39.414Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/f6/0d74d9464bfb4f39451abf745174ec0c4d5c5ebf1c2fcb7556263ae3f75a/pyobjc_framework_coreaudio-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6552624df39dbc68ff9328f244ba56f59234ecbde8455db1e617a71bc4f3dd3a", size = 38390, upload-time = "2025-11-14T09:43:43.194Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/f2/c5ca32d01c9d892bf189cfe9b17deaf996db3b4013f8a8ba9b0d22730d70/pyobjc_framework_coreaudio-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:78ea67483a5deb21625c189328152008d278fe1da4304da9fcc1babd12627038", size = 37012, upload-time = "2025-11-14T09:43:46.54Z" },
+ { url = "https://files.pythonhosted.org/packages/00/be/c3d660cef1ef874f42057a74931a7a05f581f6a647f5209bef96b372db86/pyobjc_framework_coreaudio-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8d81b0d0296ab4571a4ff302e5cdb52386e486eb8749e99b95b9141438558ca2", size = 38485, upload-time = "2025-11-14T09:43:49.883Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coremedia"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/da/7d/5ad600ff7aedfef8ba8f51b11d9aaacdf247b870bd14045d6e6f232e3df9/pyobjc_framework_coremedia-12.1.tar.gz", hash = "sha256:166c66a9c01e7a70103f3ca44c571431d124b9070612ef63a1511a4e6d9d84a7", size = 89566, upload-time = "2025-11-14T10:13:49.788Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/bc/e66de468b3777d8fece69279cf6d2af51d2263e9a1ccad21b90c35c74b1b/pyobjc_framework_coremedia-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee7b822c9bb674b5b0a70bfb133410acae354e9241b6983f075395f3562f3c46", size = 29503, upload-time = "2025-11-14T09:44:54.716Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/ae/f773cdc33c34a3f9ce6db829dbf72661b65c28ea9efaec8940364185b977/pyobjc_framework_coremedia-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:161a627f5c8cd30a5ebb935189f740e21e6cd94871a9afd463efdb5d51e255fa", size = 29396, upload-time = "2025-11-14T09:44:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/ea/aee26a475b4af8ed4152d3c50b1b8955241b8e95ae789aa9ee296953bc6a/pyobjc_framework_coremedia-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:98e885b7a092083fceaef0a7fc406a01ba7bcd3318fb927e59e055931c99cac8", size = 29414, upload-time = "2025-11-14T09:45:01.336Z" },
+ { url = "https://files.pythonhosted.org/packages/db/9d/5ff10ee0ff539e125c96b8cff005457558766f942919814c968c3367cc32/pyobjc_framework_coremedia-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:d2b84149c1b3e65ec9050a3e5b617e6c0b4cdad2ab622c2d8c5747a20f013e16", size = 29477, upload-time = "2025-11-14T09:45:04.218Z" },
+ { url = "https://files.pythonhosted.org/packages/08/e2/b890658face1290c8b6b6b53a1159c822bece248f883e42302548bef38da/pyobjc_framework_coremedia-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:737ec6e0b63414f42f7188030c85975d6d2124fbf6b15b52c99b6cc20250af4d", size = 29447, upload-time = "2025-11-14T09:45:07.17Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/9e/16981d0ee04b182481ce1e497b5e0326bad6d698fe0265bb7db72b1b26b5/pyobjc_framework_coremedia-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6a9419e0d143df16a1562520a13a389417386e2a53031530af6da60c34058ced", size = 29500, upload-time = "2025-11-14T09:45:10.506Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-quartz"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ef/dcd22b743e38b3c430fce4788176c2c5afa8bfb01085b8143b02d1e75201/pyobjc_framework_quartz-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:19f99ac49a0b15dd892e155644fe80242d741411a9ed9c119b18b7466048625a", size = 217795, upload-time = "2025-11-14T09:59:46.922Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9b/780f057e5962f690f23fdff1083a4cfda5a96d5b4d3bb49505cac4f624f2/pyobjc_framework_quartz-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7730cdce46c7e985535b5a42c31381af4aa6556e5642dc55b5e6597595e57a16", size = 218798, upload-time = "2025-11-14T10:00:01.236Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/2d/e8f495328101898c16c32ac10e7b14b08ff2c443a756a76fd1271915f097/pyobjc_framework_quartz-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:629b7971b1b43a11617f1460cd218bd308dfea247cd4ee3842eb40ca6f588860", size = 219206, upload-time = "2025-11-14T10:00:15.623Z" },
+ { url = "https://files.pythonhosted.org/packages/67/43/b1f0ad3b842ab150a7e6b7d97f6257eab6af241b4c7d14cb8e7fde9214b8/pyobjc_framework_quartz-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:53b84e880c358ba1ddcd7e8d5ea0407d760eca58b96f0d344829162cda5f37b3", size = 224317, upload-time = "2025-11-14T10:00:30.703Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/00/96249c5c7e5aaca5f688ca18b8d8ad05cd7886ebd639b3c71a6a4cadbe75/pyobjc_framework_quartz-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:42d306b07f05ae7d155984503e0fb1b701fecd31dcc5c79fe8ab9790ff7e0de0", size = 219558, upload-time = "2025-11-14T10:00:45.476Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a6/708a55f3ff7a18c403b30a29a11dccfed0410485a7548c60a4b6d4cc0676/pyobjc_framework_quartz-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0cc08fddb339b2760df60dea1057453557588908e42bdc62184b6396ce2d6e9a", size = 224580, upload-time = "2025-11-14T10:01:00.091Z" },
+]
+
+[[package]]
+name = "pyserial"
+version = "3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
+]
+
+[[package]]
+name = "pyusb"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/6b/ce3727395e52b7b76dfcf0c665e37d223b680b9becc60710d4bc08b7b7cb/pyusb-1.3.1.tar.gz", hash = "sha256:3af070b607467c1c164f49d5b0caabe8ac78dbed9298d703a8dbf9df4052d17e", size = 77281, upload-time = "2025-01-08T23:45:01.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/b8/27e6312e86408a44fe16bd28ee12dd98608b39f7e7e57884a24e8f29b573/pyusb-1.3.1-py3-none-any.whl", hash = "sha256:bf9b754557af4717fe80c2b07cc2b923a9151f5c08d17bdb5345dac09d6a0430", size = 58465, upload-time = "2025-01-08T23:45:00.029Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
+]
+
+[[package]]
+name = "reachy-mini"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "asgiref" },
+ { name = "cv2-enumerate-cameras" },
+ { name = "eclipse-zenoh" },
+ { name = "fastapi" },
+ { name = "huggingface-hub" },
+ { name = "jinja2" },
+ { name = "libusb-package" },
+ { name = "log-throttling" },
+ { name = "numpy" },
+ { name = "opencv-python" },
+ { name = "pip" },
+ { name = "psutil" },
+ { name = "pyserial" },
+ { name = "pyusb" },
+ { name = "reachy-mini-motor-controller" },
+ { name = "reachy-mini-rust-kinematics" },
+ { name = "scipy" },
+ { name = "sounddevice" },
+ { name = "soundfile" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3f/6e/0ced70f87d1482a1e10d3b1cd0d72154013890e14c8b51a41fed696b78f1/reachy_mini-1.1.1.tar.gz", hash = "sha256:dfcac20e09869284bffc5e3243e4a3556eb9efd2ca1485ce752daabe4c5f74dc", size = 24573874, upload-time = "2025-11-25T18:03:18.583Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/41/4640e247a92f10d4e727ae50d158e44b277a7ac56cf53742fc08aec14b61/reachy_mini-1.1.1-py3-none-any.whl", hash = "sha256:9371fa52686c548817659a60170139520789be177315680465b4b6122f27fd61", size = 24632472, upload-time = "2025-11-25T18:03:15.914Z" },
+]
+
+[[package]]
+name = "reachy-mini-motor-controller"
+version = "1.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4a/8d/353b13636ebffae848c0d0b8c4bfc15c4673b059f62f9c310ee98986b0b7/reachy_mini_motor_controller-1.4.2.tar.gz", hash = "sha256:bd6e9f5c13394bc57478a09e76e01c28854ae0c80a9335a3a1b78dc80e0ee150", size = 28248, upload-time = "2025-11-25T17:56:55.263Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/20/ac97ac49969c02e0384888c73cccf0b50a7bba48b18842a96b082a8c8e89/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f405d5f948abd26e423f156fc0d165b69ec964d3a4cbfa7d2d952313cc0ff972", size = 586684, upload-time = "2025-11-25T17:56:24.378Z" },
+ { url = "https://files.pythonhosted.org/packages/68/21/796b9fd0616600b93d5b74ce2b4eb0f525f2773aae8635a8c0506a4145d7/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:835e1d7e9383615b2684fb97cf18065f80498bb7d7d9ba980f8fe295cda68109", size = 572449, upload-time = "2025-11-25T17:56:17.627Z" },
+ { url = "https://files.pythonhosted.org/packages/07/bd/661f6a44bd436c069d38ba0fdc1c1524178c4d039dcd4326fb1918bccf91/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8403bb4b6c23c088493967c8445e5cd1ec4784f5c2ae1dab289fb9ba456055b", size = 642034, upload-time = "2025-11-25T17:55:56.725Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/82/12b52b5eeee59387866db9370e43894f66dda8feedc15be38a6963be3e9a/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1cf6d2a6dc10b5f8acc4c61a89e2e201eca84c6be741becbf9775084d29fc6e", size = 647692, upload-time = "2025-11-25T17:56:02.499Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/a0/4ad6ca320e303de61135219c3b9953b1ec1f7375181dbaaa98a43b65ecae/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a366aa59f9bde4f80f6c03757e431a3cea80f0b18992401169b484cf8928d14", size = 696265, upload-time = "2025-11-25T17:56:07.722Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/98/14cb24c497c295f91b11144bf639304116630de6862ba3095fe3a18dc3ff/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df475384deadcea1c8b430839addecb84126002d54649283cce5e6465d51b543", size = 660553, upload-time = "2025-11-25T17:56:12.631Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/cb/9bc5c91075d5e551deb2d93fae89062bb94b5edd5f658b66c97c11c460d8/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e09e99bb325405f414013ce7d13f7ed36af5b9278dce2ee6bda9ea2affe6baf", size = 825331, upload-time = "2025-11-25T17:56:30.458Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/fd404636cb980604ed82dea5e7ec9c4de78a76324d36ff9efc55bb7549a7/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2f35c0b54ed396b8d9eb98dfe6dcc6d3b059433c2181030591dc84483531a25b", size = 911925, upload-time = "2025-11-25T17:56:36.802Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/1c/6d48c4c3c5c9705e20265db04f040f42d1955cb0ded817e8915684999386/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:af23d02daff28470be0e775ce63666bea06ad24ce948649751f7122996d5363d", size = 854410, upload-time = "2025-11-25T17:56:43.296Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b8/55955b7b47c8ed65bda69a221cd4805bad3d07c3c95b7002e67deec43dff/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:610d74c3cfc28854137d00d206a6b4c4942a371376611781a244376fe3175277", size = 815823, upload-time = "2025-11-25T17:56:50.274Z" },
+ { url = "https://files.pythonhosted.org/packages/12/7c/20672ab40d1d25cdddf49cb969bfa36043d9b760e4e82275a32853846192/reachy_mini_motor_controller-1.4.2-cp311-cp311-win32.whl", hash = "sha256:abe319839f78cc3e4e8ed0ced0bc519f97bfc1e6ff6b320dc8f84b772123237d", size = 377969, upload-time = "2025-11-25T17:57:02.781Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/5e/0725df6e698fe9a83eff0f86d61c5e62878c864ce521c1a1c12645409bc5/reachy_mini_motor_controller-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:d8f7ea29ca78ee86206989c30066e17b213f4cc8975f0a275f092588cd2debc8", size = 401027, upload-time = "2025-11-25T17:56:57.17Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/6a/af2786310d5ccf84a48ff0153c7be2346ef3730ca540b20d998020c36cb8/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a41cc1bdc08b2fbba681804c6143319a6b4ef2d30093910f9776db01c5ba9595", size = 583808, upload-time = "2025-11-25T17:56:25.635Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/d3/6354fd9b34b89d2b5a639c56ae05f393d2ae7cef187bfcce60a71a40202d/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af3cdd5197f57108044af81166dd755214f469c368e79221521b9a698da45223", size = 570332, upload-time = "2025-11-25T17:56:18.828Z" },
+ { url = "https://files.pythonhosted.org/packages/da/eb/d30bc7517a2f7c860b95978af73beede33819e717978d5560f4b5b8a2953/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed2537a512098664d73a7575af763acd5446fc2325f37cf3b9b84d37368b5f7f", size = 641541, upload-time = "2025-11-25T17:55:58.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/63/8f9c091d0b210ebc6aa7762fa09155456a088df987d73dc39cda4fb9c241/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dd7392b9fef86cca8c12d5861d8f17e2eb7e1d69b49c65fbf54e1410f9567", size = 648054, upload-time = "2025-11-25T17:56:03.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/15/a1bebc7acfcb02c12ba32e00dc1f9346fdf2468e441f983daf86ee4c7df3/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3f6ddd91e0228b3396f2c027f3e55eac0d3c4f280968ec73f2bdac37cdeda0", size = 696773, upload-time = "2025-11-25T17:56:08.895Z" },
+ { url = "https://files.pythonhosted.org/packages/06/9f/a3ab6942383198e4c105075582c2355350ef33f90ef4f877105ada181e88/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0b357e49773bcd338e24ed17c4a93fb1890c97513c4cd07eec8c4f3ba06bc48", size = 660676, upload-time = "2025-11-25T17:56:13.77Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/09/7da09c39d2722d6323ed719de95f2f7777699b0049b81d25aaff9b0cc2db/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:dd05b4bbf2aa30674b2955edf8fefbdb90b522077b3c549719afd4a1d2dd325b", size = 824841, upload-time = "2025-11-25T17:56:31.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/68/2c4e8160a3d8e0e20a61ad491dccb9d17febbaa434da83e38060a7517fc9/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:377d47674a0429a01f509d9fe9b52365a715ca132bfdf4956a3b5bb219723941", size = 912195, upload-time = "2025-11-25T17:56:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/14/5f/a547e59efd62518afbb616c9e3bc2f057662fdd209f75b218ef1917bcd7b/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:742c1e077669c198a4261acf3213789ed679846a25714e74e9774984862bbc7a", size = 855751, upload-time = "2025-11-25T17:56:44.595Z" },
+ { url = "https://files.pythonhosted.org/packages/76/58/8720be3e52e302052c6d10e3c0937271e8c376d6e5a10b2dac480c94c7f2/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9b2eb49965f2dba93a1d251b0beb4b22b81f5b18946d53ff8761097011ffaa43", size = 815778, upload-time = "2025-11-25T17:56:51.74Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/52/7af3228652ce3503243cbce68d736af00b11afd07f0f96effa6faaca677d/reachy_mini_motor_controller-1.4.2-cp312-cp312-win32.whl", hash = "sha256:e59bd6a1b89b94da835b1fbe3f483470ea0471fdb1b92d0d4bbb93a24ab7a1aa", size = 377018, upload-time = "2025-11-25T17:57:03.815Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/7a/090705f6e62ec10107ea20e78fd8b8f26f6421b9db49f7bf5e275084aa02/reachy_mini_motor_controller-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:ae99c65942130ce04a01d50dae359694a42fa91ede864ead0defd9a693b30b05", size = 399902, upload-time = "2025-11-25T17:56:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ac/fc016600786fbcdc8d048f7e42c946aa1a488e382e08b83e39934ee5f381/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:43064e6c97be9f6fb2494ebf56dda18daa0366cd70b32ee548818c908d8b3564", size = 583599, upload-time = "2025-11-25T17:56:27.042Z" },
+ { url = "https://files.pythonhosted.org/packages/07/c7/c79c33cf46bbe6301fef5520445649ce10a85a30754fce975df41f7b80b7/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ebe3f763c011ecd3dc4bd017dfc886ecaf455d9866cab1ce8b81116d22eb6a3d", size = 570309, upload-time = "2025-11-25T17:56:20.391Z" },
+ { url = "https://files.pythonhosted.org/packages/78/e0/d301987769c1504549c06cf0ce8e132df48bde0f47d28a8768c25b7ed8ae/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c86146d3631dd73efb77d591f8a33c288b9ad394d3a6805c0685f3c5ec05266f", size = 641472, upload-time = "2025-11-25T17:55:59.645Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/52/7884ee1c04b480f8e34b4c97697a0977d314a22f6766fd5e246dfbddb404/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36052e08d331ae768f4b562e950eaa767ae4ee8a91e9334498c1bcbafe841514", size = 648510, upload-time = "2025-11-25T17:56:05.286Z" },
+ { url = "https://files.pythonhosted.org/packages/03/56/619bb25bb31f96d6172d1bfd5bbf516b5985b595f6e1c7c82481fff95c55/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b2ac6712f7a5493bb3be276fa53bb9b9e4770f13783f26ff0a18e7fc6f800d4", size = 697241, upload-time = "2025-11-25T17:56:09.99Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/b2/c82a31cf252ff6fb4d4c59a208d8375159cc796326812b7e946eea5c00a4/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fcb404dc2021175d4339fb3a8f9334cd999bf8a0369e0dacb02c430edf46a0b", size = 660207, upload-time = "2025-11-25T17:56:15.23Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6e/b2d9aecc4144cffcee14b023da20b8850a11cd35375561a7e60be7132e10/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f482f61eecdeb72a7722399d0d549872a239023c263968f9e2ffac9ee69687dc", size = 824941, upload-time = "2025-11-25T17:56:33.129Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/68/72b7d8a405fe7b1785b2caaaf917958dc6691015f6c15089bfee8b35f65d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:88e1a5c1d4afe4e695d589715f0628536fe7399ec6d2751025dd785f4282a62e", size = 912805, upload-time = "2025-11-25T17:56:39.585Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/62/4ce77e08ea5dc428835202723671026f52d46e20ee2642af24ecd2c64858/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2d5c9e2645d7c6b29a8a46b4ecef72540bb2c0f983349cfcaf7c681386e7a002", size = 856256, upload-time = "2025-11-25T17:56:45.894Z" },
+ { url = "https://files.pythonhosted.org/packages/71/a6/c031586c9dd8fc735b427c5fcdd20d9360c02a6c61ab3cbb59589ad15d9d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffb5331150c0cdfac9910d341e82d788bc173e473e893e3f8e54938ede1e3523", size = 815565, upload-time = "2025-11-25T17:56:52.837Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/e3/5316b60491ecf3384b1b51db49317bc175561411edcc83fe1398b68736f4/reachy_mini_motor_controller-1.4.2-cp313-cp313-win32.whl", hash = "sha256:6edcf88f5d4c1bf3948328a6f65cd4518fda8cefbe55fb884fda088f363c2b60", size = 377394, upload-time = "2025-11-25T17:57:05.059Z" },
+ { url = "https://files.pythonhosted.org/packages/19/c7/41f4efca4abaf31bff5fa891ae0f594623539afd936abea51e0e546a6700/reachy_mini_motor_controller-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:602cf3294f54ceae4fa84d006aa0e97dcfcd99f7ee556cbbcf3546ac42fe8fb3", size = 399788, upload-time = "2025-11-25T17:56:59.383Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/0b/3b118d1ed20da22c3844c88247a78f6fdee679fff149d0e1ee64fc5d8aa8/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:aedcbebb270c3e6ad8898aac321f82a3d3060898ee33d75ae181b0f1df278f93", size = 581082, upload-time = "2025-11-25T17:56:28.189Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e7/c24da5194f16714d6f2a67fefaba817e2a22f68f58269500fa4495d6a29e/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d8d7626e3655a96266cc88d9ae1327c8b42512b61e8acaeb782d8d3352125c2", size = 567918, upload-time = "2025-11-25T17:56:21.842Z" },
+ { url = "https://files.pythonhosted.org/packages/89/69/cc9a51e04c1523213a8f4121bb1c454293d287f8c0fc4998e2640d053169/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8b27d6769f12a4bf75b8b6cb0e0dc20d154bc1422ff9e046f530434050552b86", size = 823910, upload-time = "2025-11-25T17:56:34.482Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/76/1f283234ec0fb1b349a24930485a9c584d94f064eb4d3d2e32e5607ec64e/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:07118c8cafed873541fb716928a3d4f807ac6ac7f769d7307faa4cd9bead6e6d", size = 910812, upload-time = "2025-11-25T17:56:40.804Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/22/771e3e02872c319f999c5e0830787c926d2b74b976c71f041d5429c4f785/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d552fdf890c3040232b2db783fc03ffcee230b633208e62b7a4265fa5d076598", size = 856433, upload-time = "2025-11-25T17:56:47.095Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/a3/c897ed4e7a43d205d43a985669dfbbc3943d1df8a101038c5384f34b9617/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b250a7763a72d6864ef4cdcec088d56e4d17947a10493f7347f2001502f05ed", size = 816617, upload-time = "2025-11-25T17:56:54.099Z" },
+ { url = "https://files.pythonhosted.org/packages/03/67/63dc13fe0fad1bafe12122b10479b2e22bb4562e58ed45e14f18b3f076c5/reachy_mini_motor_controller-1.4.2-cp314-cp314-win32.whl", hash = "sha256:d9a6be4852cb8cbec2e19d2037234dce39defce653f3af9d4a46cd56d624c617", size = 376862, upload-time = "2025-11-25T17:57:06.232Z" },
+ { url = "https://files.pythonhosted.org/packages/32/08/0c4b6fc4a761ad8bd362f571d3693c94aa0c15121e122f1c01e3f38bc873/reachy_mini_motor_controller-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:aa7b6ca1b6db09e6249fdb5a31376974e82928292950cd7df4480cbd5707a425", size = 400104, upload-time = "2025-11-25T17:57:00.616Z" },
+]
+
+[[package]]
+name = "reachy-mini-rust-kinematics"
+version = "1.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/82/485aa2f28973eee6198b2af0db9e76314fd909076f06782228fa686f0e3d/reachy_mini_rust_kinematics-1.0.2.tar.gz", hash = "sha256:ca14f9193c73a53612a32cd0bfb9abecb13343bc94fe5feb95f54736b7f3cd64", size = 17187, upload-time = "2025-10-22T08:35:50.104Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2e/fd/c3801f0579da40147c7cadc1226bc3f5b7413cc590b13f53822674a910f7/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9fe481d3ae29415e9be3358493bd6d26add4b494650384c650181595da10204", size = 303080, upload-time = "2025-10-22T08:34:54.167Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/1b28508a05fde9bf80277222991273721462afa84a7a0ede6227692b6a75/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:58c87c6cb62a020f325caf8344f003ba4ebb783e20f417c6d89e3b63a72bc8a1", size = 278691, upload-time = "2025-10-22T08:34:44.295Z" },
+ { url = "https://files.pythonhosted.org/packages/de/22/9b4c4b007085db8fb329656146742414e64051d9deb8e108020fbb3aafa8/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af2f330760b5519dcf556c728ddaa889fbc1b5ef6d76a4f3ef2eee1ddd032cb4", size = 312649, upload-time = "2025-10-22T08:34:02.52Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/e7/b50b441d287e72864e32b1049ab6d740158a8330deba5ec2cfa62d5125e0/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8bffa448b0a75a570c5b8625344de3a755bca7f3a7450b045df153cef0cb402", size = 321102, upload-time = "2025-10-22T08:34:14.669Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/4b/cddf709a7012450d3255d7ab6ee6a1216e761664657ffe68806712d95c42/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ae0811ccc4132f9b034771af061c513bcb0bd18a73dad1d16abc6bad366caa", size = 337443, upload-time = "2025-10-22T08:34:35.544Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6d/908fca70c2dee3e066831fcde011fb0d8c563418c75e052208bc538105cb/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5777d235930be71186adf948388580317943a022aa3761d6f78bcffe7dfc5ee4", size = 351864, upload-time = "2025-10-22T08:34:26.064Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/e8/ddbfbc71cc2a7246de06cd4a0e934c9613a3a814406afbf8dfcd071813bf/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cbf226d07d34a6a395635b90ebb4d6e083136f07ca2e746d4431c44b00fd2d5", size = 493208, upload-time = "2025-10-22T08:35:01.445Z" },
+ { url = "https://files.pythonhosted.org/packages/75/d1/51af83f5b70c2eaf5f871efdf62c7d88116af2d5309898b2de9925857556/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bab5d550e2225c9d4d6ee69b98fd13cf75b8b3df4ab9824d7759668e04a6e5bf", size = 585005, upload-time = "2025-10-22T08:35:14.756Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/a8/cef7d415d864c8fa04362485ecb1648947a194b90426d147972ce59f4263/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7fc0243f9abc3d14aadaaa19b86d4930bf1e3bace0bfbbde73048ed80928b0f5", size = 526789, upload-time = "2025-10-22T08:35:27.367Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/4e/98e8e72f3cf0758eb4e6eb7320438dfb9e08d6ff593f6cc3661e726cf9a1/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a060b9022becd8decf9868063099fe68e598f7c07406d96795408ca862d208b3", size = 498731, upload-time = "2025-10-22T08:35:38.944Z" },
+ { url = "https://files.pythonhosted.org/packages/26/fc/d9b66b439ea27c20d01e6cc841b34b3a8cc17fb0e79acaaa8ba4f5a4c587/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win32.whl", hash = "sha256:ebf961d3ae3cd0798ed8d4e2bec8aec57f33fe2fb713852ad0ba2de4386c9c1a", size = 176607, upload-time = "2025-10-22T08:35:58.496Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/be/848776e83114ee92d9799860860be6dff036f22c9d01e85805c02a6d05fc/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:8afa30ed7da49544c8215033d2375c2f76f3311a6a1f380fe9acd2c076f9d45f", size = 187332, upload-time = "2025-10-22T08:35:51.994Z" },
+ { url = "https://files.pythonhosted.org/packages/11/39/6c40cca1970ce47d9ae1661c87230cd37a6e2d782f34e0aec745f155d831/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:356134c5df18a01226432fbd1fb24649aced5a14da3c0406ebe2d1b3b8c002d2", size = 299587, upload-time = "2025-10-22T08:34:55.208Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/30/f9ba417bc1782d6a256ef9024eb0329c50cb7a3d12e83afd4b67047a01d6/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a232408fb7c58d6da73c40a40a120bae4f51bf9119de108530572723a00280d7", size = 276656, upload-time = "2025-10-22T08:34:46.904Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e7/e3bb2597d86f2037a1e4537f2352d7e92ff4699ee725b43a90400f15894e/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e7c3d2c73f89bf817d8c3abb8f0b23a6efdfce330a749c6e29e1e0cf6db9d7d", size = 312798, upload-time = "2025-10-22T08:34:04.219Z" },
+ { url = "https://files.pythonhosted.org/packages/62/2f/64d2c4b434ef413c948935a0f57b4a502cc7effad68dd97263cdee22df64/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba0faba18be71b68026c70ccf4db6d9ea588774053d22487f228b490204a8a00", size = 321023, upload-time = "2025-10-22T08:34:15.628Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/0e/f4cbddd5c0ed10cdba7f4baffc68f2e8102eb1d55b309690a52af3b067f8/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bed2ec420ea354ce62572afe8672ae8701a139781d9d8da97932597907019ee", size = 337756, upload-time = "2025-10-22T08:34:36.66Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/d5/ac043e65949ba904b7ac901d74b8514ab47203de3af44e96c44dd2da7a94/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a79f6517728463294d5ca4945b18d843dd16cc410815ce039b35c1198bdc355d", size = 351251, upload-time = "2025-10-22T08:34:27.06Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ef/80bb31cee07d2336013808ce3b76dee29ae04274f053e1694e3a285da555/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5404cf14cc8894962dbbc82594af453588539b4b9458b8f95f43c29bc8bd2e34", size = 493292, upload-time = "2025-10-22T08:35:02.56Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/e2/4f6c5a0d2a77111a9806243160b1aa3531ad3024a59f05c7f6157917bcc5/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2e1f9d9ce4222d7ecda5cb77c96fed763de3ac94993e60c73adf9749d26e16df", size = 584893, upload-time = "2025-10-22T08:35:15.981Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/b6/b0b57c9c46b77aa3c03f4071fca0c92809e23f9b22df5f85446ec5329fe2/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7c29c3d7b70d0576c740969cfa3e35ba98cc8e378c0867a79f53d5fee3568f77", size = 525948, upload-time = "2025-10-22T08:35:28.451Z" },
+ { url = "https://files.pythonhosted.org/packages/06/12/f4d94e8c0b1b19b47072c2041f31fb83701cff1a199d0fcf012c0489f32f/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b94ca8e94058efcd89553144b0a0b0f7c4abfe67a4cd60e2993cd0833c9e130", size = 499239, upload-time = "2025-10-22T08:35:40.026Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b6/907870cb3f588a6da58b64357c03241a28dac8a9ec9f42e91c291cb95c51/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win32.whl", hash = "sha256:edcbcedaa4aa99684588bd897733a33ff8f8d9eed8ae39bd7d0fd691874f0457", size = 176379, upload-time = "2025-10-22T08:35:59.841Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/c0/a5c1db0bf8dc55cf6b41a11463285ef440a2efa74d8b75be8443234c1acf/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:eb5d6d27ce08e8816496b6049979cf64ffbf3901dd5bb290efe439f5455526c2", size = 187469, upload-time = "2025-10-22T08:35:53.307Z" },
+ { url = "https://files.pythonhosted.org/packages/13/5c/fffb02be21f666cf880042804c703de2410f32a44b0412d6df20bb156d15/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2aae2b9424a111935e26977fe2ab25c130c766e8049d72073e31a7ad78c84fb5", size = 299868, upload-time = "2025-10-22T08:34:56.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/33/b7e610c6325ed4fe439bd0be3e96850b5a5b6dc360116741bdf4c3a57ce9/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38ba383417c472ac67945a57c62ba621128b4486bd9d818512929454f47ce76d", size = 276655, upload-time = "2025-10-22T08:34:47.905Z" },
+ { url = "https://files.pythonhosted.org/packages/93/bf/a1f579e4a6930b2155c99f80ac5b96d256c0047537c0802c34f0adfcde81/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3788d9c5b5315eff026486c3367e97ea23cd4ccd3eb6c7c1e37e0d00ec6b7128", size = 312962, upload-time = "2025-10-22T08:34:05.504Z" },
+ { url = "https://files.pythonhosted.org/packages/89/cf/bf2a450adb2e6fbc5a9baa3ad9bece4d7272341723f4e76eff710463b484/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6983b555c1af6fd2c1ca8b32ae374c2b060239bb04b9b562806771ddafe81abf", size = 320871, upload-time = "2025-10-22T08:34:16.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/ad/1eab50e3c56a65ac2f14a4890ae5370984586f52adf13cd8a7963ebf423c/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca28a8cda1ab4973259ed9aa06c7173ac5899492a3140075c58225a0ed96be52", size = 337653, upload-time = "2025-10-22T08:34:37.679Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/ce/8d3802dde54f9595daa9bd7b12a3de42ba4b4b48d92d8abe7a43919d74a8/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c8591616ef354d4f0724affef58eb14374f4a06b649b7947e94b97ec22d2fb9", size = 351553, upload-time = "2025-10-22T08:34:28.126Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8c/5e1de72d044440afb8cb7480af0c9ddcdcd9654b0ac62c11113fedc731d4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45f4d9f159714e60eef22c9c00232beb34a8d27a89acce7d3ffcec2b86992b06", size = 493556, upload-time = "2025-10-22T08:35:03.963Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8b/1a33d934b9720586dc261349b500fb6a969ff1d3ba7c6f9bb7ba87d61d37/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:02a525696238dc85e9a1f3260d7d0db25074bb54f1cc2a0a4052849344026806", size = 585019, upload-time = "2025-10-22T08:35:17.344Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/5e/8cf4011456e3262f2a67b66ee77838cc56655cdcd7941efd44cb6ff8eb85/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:342b4a9adb9714f98d6569121c053800ae0a5b3904a9863ca65603bb3b9e7f0a", size = 526143, upload-time = "2025-10-22T08:35:29.636Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/3e/0d1168324c288693c07abd0050b83f682cf12a27c850e42d2601d3da84ef/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b596910a1ba0aaf79fc525ec5a8405dcd41c28946667faa549e789e5c1b93a5", size = 499284, upload-time = "2025-10-22T08:35:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/66/4e/0f69c0287a9289a0bc78d623cbf8cbd652e54ff6e854979b4985eb7fa75e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win32.whl", hash = "sha256:d5437b5d7f9093e879859d83da6888738f281fe728fad0a9f1678fe01035b4d3", size = 176631, upload-time = "2025-10-22T08:36:00.974Z" },
+ { url = "https://files.pythonhosted.org/packages/94/00/eff3fa6480da3c2b486b8bcec55ea650b4955fb874664e9e7dce205a9885/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:c3c813012eacc7e779c81388f3dcb30811bee461672f9f9dc03835c73e3a5bc4", size = 187334, upload-time = "2025-10-22T08:35:54.464Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/e2/2c19fbf08a5e3836147ee345b9d75fbe1cd26df8d1f9e83b9cefa4bc80ae/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e1f563b31cbb0e0ee9f015801ef2eba31b9c37c306e3ca7737097a482355e0a", size = 312043, upload-time = "2025-10-22T08:34:06.505Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d4/873c4317a598e9709dd2aa68f0a072568b1f2db3616a2ffa76259618889e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:874b986c44a94e1e1752f8f0b159342b0f60d4610e4df9c4bdf421e2749d949e", size = 320190, upload-time = "2025-10-22T08:34:17.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/11/64941229b39bfb1c39ea2ccb5fd02998915f87e40e277e4b0b744a2c3a2b/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f5060865aba96d3ffe1ac5cb93dc6439154a78ef8fdbb8a4313e4dc33064efb5", size = 492437, upload-time = "2025-10-22T08:35:05.014Z" },
+ { url = "https://files.pythonhosted.org/packages/20/47/82704725e0d5cae887a4604e9bbf7688e6a676768ab47202b018ef9f7f32/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f7ddeef8610a936afbfd7dda1713be18d207032e18604e46963729996c3d1a41", size = 583980, upload-time = "2025-10-22T08:35:18.517Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6d/bfcdc6912531c62885450646f629aa581fb116ee5806dba00c01712b02f4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2f498398c933e87a63a5747a2c813df09dcb68ded3ba79f86d092801ced36da8", size = 525181, upload-time = "2025-10-22T08:35:30.819Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/6d/4f32bc65f83307a6eba5b62231e276bde8509d26dd8ba1a9997fb6c4b924/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6464868b209ec5cc0e50829b25bdad8cf765a48c943a5a7f504a3ec4ed7aedcb", size = 498743, upload-time = "2025-10-22T08:35:42.183Z" },
+ { url = "https://files.pythonhosted.org/packages/85/26/2af509eb72c556a5f8628454122f61bcd55ae760ed74c2a1d8cfe21ebafe/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:419cba0b2ca01b52aba79004681201e1ad16d5a06a2ae9f4f74c45ea82e88f11", size = 276979, upload-time = "2025-10-22T08:34:49.023Z" },
+ { url = "https://files.pythonhosted.org/packages/82/62/5fdf253c4293447fe2b95329891a46292fed02fd15f71a7eafbcca842194/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c654f6e06da8c6f8f5f2613c7a8d1841dca730ba2397125418f1b26bfeb46330", size = 337650, upload-time = "2025-10-22T08:34:38.745Z" },
+ { url = "https://files.pythonhosted.org/packages/01/2e/eee9bed34de89569f52965fdbf6bd16c914c1b87f4f233e49ffa1f3f0590/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:58a36591b27a80c0836ab08ff21bf605413213f079baeef6fd1e3d6b47d8a341", size = 352082, upload-time = "2025-10-22T08:34:29.451Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/2d/c4492e19625f81348c812e5e17e97e777c521fe7200006bc41ebc904dd14/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305793e3097db1a93eabeeb5bcf7a581dedb859740d5f5164d7f2b695ce90953", size = 312932, upload-time = "2025-10-22T08:34:11.265Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/b2/d6cdc7960650c609b3e902734dcd10d6eb44373ba4f0aadaefde401244ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dda63846d7196737bad42d9de847b3c4702e68e40fc0a691fed50a87b9ef0898", size = 321183, upload-time = "2025-10-22T08:34:22.468Z" },
+ { url = "https://files.pythonhosted.org/packages/51/74/d92143a61237d3f83ba9f6398f7d546cdefd8d43f820677bcf4d7ed3a265/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b307111f6ff46a1e8dc5b0d600c836ccf237106cf0bb9b87d85e079199fb733", size = 337577, upload-time = "2025-10-22T08:34:42.092Z" },
+ { url = "https://files.pythonhosted.org/packages/05/e4/5ab1e64bd201e388f3fc0bb1ee89ef166479c8482d582ee58d603ba1b6ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64649a8ca74f3f528d57adf02f3b5ad6eaab4e5ab40bf837554ad878f0f4ea64", size = 351547, upload-time = "2025-10-22T08:34:33.109Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/19/f754c4cac653d3133145694abb6bb2d0da15b2459c1361b35528b3d16287/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6f0eda0e6cb8d86e80937a78b44e3db5e338039480fc077503aca3c23af008e6", size = 493554, upload-time = "2025-10-22T08:35:10.535Z" },
+ { url = "https://files.pythonhosted.org/packages/af/81/14e1848dc1fb662a9dd8108bb3be4161a241055d38bac65eb9ee1fe5926c/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:12b9c538544352ee829338b3ae34623e3903294cd09ebb286f9467cad9882360", size = 585142, upload-time = "2025-10-22T08:35:23.413Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/47/a74608d37b58a162a3204ad4a1b7981ca79dc598b6c76533922cdc2bcb48/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:56eb81ad90e2b04506b4adf809b48a449fbd3972e2b32bd49038b745a6ff2d38", size = 526427, upload-time = "2025-10-22T08:35:35.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/8e/c48f621c8d954849deb081a7a8840c57e38385dad999aaf5b82e10a09f84/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2af9062ffb5970b3318a85eaa7ce78042e7b023e7c096e34002da5aa083920dd", size = 499551, upload-time = "2025-10-22T08:35:47.616Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "rmscript"
+version = "0.1.0"
+source = { directory = "../../rmscript" }
+dependencies = [
+ { name = "numpy" },
+ { name = "reachy-mini" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "numpy", specifier = ">=1.24.0" },
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" },
+ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" },
+ { name = "reachy-mini", specifier = ">=1.0.0" },
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" },
+ { name = "scipy", marker = "extra == 'dev'", specifier = ">=1.10.0" },
+]
+provides-extras = ["dev"]
+
+[[package]]
+name = "rmscript-web-backend"
+version = "1.0.0"
+source = { editable = "." }
+dependencies = [
+ { name = "fastapi" },
+ { name = "pydantic" },
+ { name = "rmscript" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "fastapi", specifier = ">=0.104.0" },
+ { name = "pydantic", specifier = ">=2.4.0" },
+ { name = "rmscript", extras = ["reachy", "scipy"], directory = "../../rmscript" },
+ { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.16.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" },
+ { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" },
+ { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" },
+ { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" },
+ { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" },
+ { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" },
+ { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" },
+ { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" },
+ { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" },
+ { url = "https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c", size = 36617856, upload-time = "2025-10-28T17:33:31.375Z" },
+ { url = "https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d", size = 28874306, upload-time = "2025-10-28T17:33:36.516Z" },
+ { url = "https://files.pythonhosted.org/packages/15/65/3a9400efd0228a176e6ec3454b1fa998fbbb5a8defa1672c3f65706987db/scipy-1.16.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9", size = 20865371, upload-time = "2025-10-28T17:33:42.094Z" },
+ { url = "https://files.pythonhosted.org/packages/33/d7/eda09adf009a9fb81827194d4dd02d2e4bc752cef16737cc4ef065234031/scipy-1.16.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4", size = 23524877, upload-time = "2025-10-28T17:33:48.483Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/6b/3f911e1ebc364cb81320223a3422aab7d26c9c7973109a9cd0f27c64c6c0/scipy-1.16.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959", size = 33342103, upload-time = "2025-10-28T17:33:56.495Z" },
+ { url = "https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88", size = 35697297, upload-time = "2025-10-28T17:34:04.722Z" },
+ { url = "https://files.pythonhosted.org/packages/04/e1/6496dadbc80d8d896ff72511ecfe2316b50313bfc3ebf07a3f580f08bd8c/scipy-1.16.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234", size = 36021756, upload-time = "2025-10-28T17:34:13.482Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/bd/a8c7799e0136b987bda3e1b23d155bcb31aec68a4a472554df5f0937eef7/scipy-1.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d", size = 38696566, upload-time = "2025-10-28T17:34:22.384Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304", size = 38529877, upload-time = "2025-10-28T17:35:51.076Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/14/9d9fbcaa1260a94f4bb5b64ba9213ceb5d03cd88841fe9fd1ffd47a45b73/scipy-1.16.3-cp313-cp313-win_arm64.whl", hash = "sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2", size = 25455366, upload-time = "2025-10-28T17:35:59.014Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a3/9ec205bd49f42d45d77f1730dbad9ccf146244c1647605cf834b3a8c4f36/scipy-1.16.3-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b", size = 37027931, upload-time = "2025-10-28T17:34:31.451Z" },
+ { url = "https://files.pythonhosted.org/packages/25/06/ca9fd1f3a4589cbd825b1447e5db3a8ebb969c1eaf22c8579bd286f51b6d/scipy-1.16.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079", size = 29400081, upload-time = "2025-10-28T17:34:39.087Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/56/933e68210d92657d93fb0e381683bc0e53a965048d7358ff5fbf9e6a1b17/scipy-1.16.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a", size = 21391244, upload-time = "2025-10-28T17:34:45.234Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/7e/779845db03dc1418e215726329674b40576879b91814568757ff0014ad65/scipy-1.16.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119", size = 23929753, upload-time = "2025-10-28T17:34:51.793Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/4b/f756cf8161d5365dcdef9e5f460ab226c068211030a175d2fc7f3f41ca64/scipy-1.16.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c", size = 33496912, upload-time = "2025-10-28T17:34:59.8Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b5/222b1e49a58668f23839ca1542a6322bb095ab8d6590d4f71723869a6c2c/scipy-1.16.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e", size = 35802371, upload-time = "2025-10-28T17:35:08.173Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/8d/5964ef68bb31829bde27611f8c9deeac13764589fe74a75390242b64ca44/scipy-1.16.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135", size = 36190477, upload-time = "2025-10-28T17:35:16.7Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/f2/b31d75cb9b5fa4dd39a0a931ee9b33e7f6f36f23be5ef560bf72e0f92f32/scipy-1.16.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6", size = 38796678, upload-time = "2025-10-28T17:35:26.354Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/1e/b3723d8ff64ab548c38d87055483714fefe6ee20e0189b62352b5e015bb1/scipy-1.16.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc", size = 38640178, upload-time = "2025-10-28T17:35:35.304Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/f3/d854ff38789aca9b0cc23008d607ced9de4f7ab14fa1ca4329f86b3758ca/scipy-1.16.3-cp313-cp313t-win_arm64.whl", hash = "sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a", size = 25803246, upload-time = "2025-10-28T17:35:42.155Z" },
+ { url = "https://files.pythonhosted.org/packages/99/f6/99b10fd70f2d864c1e29a28bbcaa0c6340f9d8518396542d9ea3b4aaae15/scipy-1.16.3-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6", size = 36606469, upload-time = "2025-10-28T17:36:08.741Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/74/043b54f2319f48ea940dd025779fa28ee360e6b95acb7cd188fad4391c6b/scipy-1.16.3-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657", size = 28872043, upload-time = "2025-10-28T17:36:16.599Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/e1/24b7e50cc1c4ee6ffbcb1f27fe9f4c8b40e7911675f6d2d20955f41c6348/scipy-1.16.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26", size = 20862952, upload-time = "2025-10-28T17:36:22.966Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3a/3e8c01a4d742b730df368e063787c6808597ccb38636ed821d10b39ca51b/scipy-1.16.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc", size = 23508512, upload-time = "2025-10-28T17:36:29.731Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/60/c45a12b98ad591536bfe5330cb3cfe1850d7570259303563b1721564d458/scipy-1.16.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22", size = 33413639, upload-time = "2025-10-28T17:36:37.982Z" },
+ { url = "https://files.pythonhosted.org/packages/71/bc/35957d88645476307e4839712642896689df442f3e53b0fa016ecf8a3357/scipy-1.16.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc", size = 35704729, upload-time = "2025-10-28T17:36:46.547Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/15/89105e659041b1ca11c386e9995aefacd513a78493656e57789f9d9eab61/scipy-1.16.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0", size = 36086251, upload-time = "2025-10-28T17:36:55.161Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/87/c0ea673ac9c6cc50b3da2196d860273bc7389aa69b64efa8493bdd25b093/scipy-1.16.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800", size = 38716681, upload-time = "2025-10-28T17:37:04.1Z" },
+ { url = "https://files.pythonhosted.org/packages/91/06/837893227b043fb9b0d13e4bd7586982d8136cb249ffb3492930dab905b8/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d", size = 39358423, upload-time = "2025-10-28T17:38:20.005Z" },
+ { url = "https://files.pythonhosted.org/packages/95/03/28bce0355e4d34a7c034727505a02d19548549e190bedd13a721e35380b7/scipy-1.16.3-cp314-cp314-win_arm64.whl", hash = "sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f", size = 26135027, upload-time = "2025-10-28T17:38:24.966Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6f/69f1e2b682efe9de8fe9f91040f0cd32f13cfccba690512ba4c582b0bc29/scipy-1.16.3-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c", size = 37028379, upload-time = "2025-10-28T17:37:14.061Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/2d/e826f31624a5ebbab1cd93d30fd74349914753076ed0593e1d56a98c4fb4/scipy-1.16.3-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40", size = 29400052, upload-time = "2025-10-28T17:37:21.709Z" },
+ { url = "https://files.pythonhosted.org/packages/69/27/d24feb80155f41fd1f156bf144e7e049b4e2b9dd06261a242905e3bc7a03/scipy-1.16.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d", size = 21391183, upload-time = "2025-10-28T17:37:29.559Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d3/1b229e433074c5738a24277eca520a2319aac7465eea7310ea6ae0e98ae2/scipy-1.16.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa", size = 23930174, upload-time = "2025-10-28T17:37:36.306Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/d9e148b0ec680c0f042581a2be79a28a7ab66c0c4946697f9e7553ead337/scipy-1.16.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8", size = 33497852, upload-time = "2025-10-28T17:37:42.228Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/22/4e5f7561e4f98b7bea63cf3fd7934bff1e3182e9f1626b089a679914d5c8/scipy-1.16.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353", size = 35798595, upload-time = "2025-10-28T17:37:48.102Z" },
+ { url = "https://files.pythonhosted.org/packages/83/42/6644d714c179429fc7196857866f219fef25238319b650bb32dde7bf7a48/scipy-1.16.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146", size = 36186269, upload-time = "2025-10-28T17:37:53.72Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/70/64b4d7ca92f9cf2e6fc6aaa2eecf80bb9b6b985043a9583f32f8177ea122/scipy-1.16.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d", size = 38802779, upload-time = "2025-10-28T17:37:59.393Z" },
+ { url = "https://files.pythonhosted.org/packages/61/82/8d0e39f62764cce5ffd5284131e109f07cf8955aef9ab8ed4e3aa5e30539/scipy-1.16.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7", size = 39471128, upload-time = "2025-10-28T17:38:05.259Z" },
+ { url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
+]
+
+[[package]]
+name = "sounddevice"
+version = "0.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload-time = "2024-10-12T09:40:12.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload-time = "2024-10-12T09:40:05.605Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload-time = "2024-10-12T09:40:07.436Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload-time = "2024-10-12T09:40:09.355Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload-time = "2024-10-12T09:40:11.065Z" },
+]
+
+[[package]]
+name = "soundfile"
+version = "0.13.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e1/41/9b873a8c055582859b239be17902a85339bec6a30ad162f98c9b0288a2cc/soundfile-0.13.1.tar.gz", hash = "sha256:b2c68dab1e30297317080a5b43df57e302584c49e2942defdde0acccc53f0e5b", size = 46156, upload-time = "2025-01-25T09:17:04.831Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/28/e2a36573ccbcf3d57c00626a21fe51989380636e821b341d36ccca0c1c3a/soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445", size = 25751, upload-time = "2025-01-25T09:16:44.235Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/ab/73e97a5b3cc46bba7ff8650a1504348fa1863a6f9d57d7001c6b67c5f20e/soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33", size = 1142250, upload-time = "2025-01-25T09:16:47.583Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/e5/58fd1a8d7b26fc113af244f966ee3aecf03cb9293cb935daaddc1e455e18/soundfile-0.13.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:743f12c12c4054921e15736c6be09ac26b3b3d603aef6fd69f9dde68748f2593", size = 1101406, upload-time = "2025-01-25T09:16:49.662Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ae/c0e4a53d77cf6e9a04179535766b3321b0b9ced5f70522e4caf9329f0046/soundfile-0.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9c9e855f5a4d06ce4213f31918653ab7de0c5a8d8107cd2427e44b42df547deb", size = 1235729, upload-time = "2025-01-25T09:16:53.018Z" },
+ { url = "https://files.pythonhosted.org/packages/57/5e/70bdd9579b35003a489fc850b5047beeda26328053ebadc1fb60f320f7db/soundfile-0.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:03267c4e493315294834a0870f31dbb3b28a95561b80b134f0bd3cf2d5f0e618", size = 1313646, upload-time = "2025-01-25T09:16:54.872Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/df/8c11dc4dfceda14e3003bb81a0d0edcaaf0796dd7b4f826ea3e532146bba/soundfile-0.13.1-py2.py3-none-win32.whl", hash = "sha256:c734564fab7c5ddf8e9be5bf70bab68042cd17e9c214c06e365e20d64f9a69d5", size = 899881, upload-time = "2025-01-25T09:16:56.663Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e9/6b761de83277f2f02ded7e7ea6f07828ec78e4b229b80e4ca55dd205b9dc/soundfile-0.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:1e70a05a0626524a69e9f0f4dd2ec174b4e9567f4d8b6c11d38b5c289be36ee9", size = 1019162, upload-time = "2025-01-25T09:16:59.573Z" },
+]
+
+[[package]]
+name = "starlette"
+version = "0.50.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.38.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.22.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" },
+ { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" },
+ { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" },
+ { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" },
+ { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" },
+ { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" },
+ { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" },
+ { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" },
+ { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" },
+ { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" },
+ { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" },
+ { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" },
+ { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" },
+ { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" },
+ { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" },
+ { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" },
+ { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" },
+ { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" },
+ { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" },
+ { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" },
+ { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" },
+ { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" },
+ { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" },
+ { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" },
+ { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" },
+ { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" },
+ { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" },
+ { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" },
+ { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" },
+ { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" },
+ { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" },
+ { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" },
+ { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" },
+ { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" },
+ { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" },
+ { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" },
+ { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" },
+ { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" },
+ { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" },
+ { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" },
+ { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
+]
+
+[[package]]
+name = "yarl"
+version = "1.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" },
+ { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" },
+ { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" },
+ { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" },
+ { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" },
+ { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" },
+ { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" },
+ { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" },
+ { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" },
+ { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" },
+ { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" },
+ { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" },
+ { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" },
+ { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" },
+ { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" },
+ { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" },
+ { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" },
+ { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" },
+ { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" },
+ { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" },
+ { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" },
+ { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" },
+ { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" },
+ { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" },
+ { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" },
+ { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" },
+ { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" },
+ { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" },
+ { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" },
+ { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" },
+ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
+]
diff --git a/frontend/app.js b/frontend/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..b23d833dd4124491dc206f3eb2a1b56db2c06a2b
--- /dev/null
+++ b/frontend/app.js
@@ -0,0 +1,436 @@
+// RMScript Web Demo - Frontend Application
+// Connects to:
+// - Backend API for compilation/verification
+// - Robot WebSocket (localhost:8000) for execution
+
+// Auto-detect backend URL based on environment
+// If opened as file:// (local development), use localhost:8001
+// If served from web server (production/Space), use relative URLs
+const BACKEND_URL = window.location.protocol === 'file:'
+ ? 'http://localhost:8001'
+ : ''; // Use relative URLs when served from web server
+const ROBOT_WS_URL = 'ws://localhost:8000/api/move/ws/set_target';
+
+// Global state
+const state = {
+ robotWs: null,
+ robotConnected: false,
+ currentIR: null,
+ isExecuting: false
+};
+
+// DOM elements
+const elements = {
+ editor: document.getElementById('editor'),
+ compileStatus: document.getElementById('compileStatus'),
+ robotStatus: document.getElementById('robotStatus'),
+ console: document.getElementById('console'),
+ irDisplay: document.getElementById('irDisplay'),
+ executionInfo: document.getElementById('executionInfo'),
+ executeBtn: document.getElementById('executeBtn')
+};
+
+// Example scripts
+const examples = {
+ basic: `DESCRIPTION Simple head movement
+look left
+wait 1s
+look right
+wait 1s
+look center`,
+
+ complex: `DESCRIPTION Wave hello
+look left
+antenna both up
+wait 1s
+look right
+antenna both down
+wait 0.5s
+look center`,
+
+ repeat: `DESCRIPTION Repeat demo
+REPEAT 3
+ look left
+ wait 0.5s
+ look right
+ wait 0.5s
+END
+look center`
+};
+
+// Initialize robot WebSocket connection
+function connectRobotWebSocket() {
+ log('Connecting to robot...', 'info');
+
+ state.robotWs = new WebSocket(ROBOT_WS_URL);
+
+ state.robotWs.onopen = () => {
+ state.robotConnected = true;
+ updateRobotStatus(true);
+ log('Robot connected!', 'success');
+ };
+
+ state.robotWs.onclose = () => {
+ state.robotConnected = false;
+ updateRobotStatus(false);
+ log('Robot disconnected. Reconnecting...', 'warning');
+ setTimeout(connectRobotWebSocket, 2000);
+ };
+
+ state.robotWs.onerror = (error) => {
+ log(`Robot connection error: ${error}`, 'error');
+ };
+
+ state.robotWs.onmessage = (event) => {
+ try {
+ const message = JSON.parse(event.data);
+ if (message.status === 'error') {
+ log(`Robot error: ${message.detail}`, 'error');
+ }
+ } catch (e) {
+ console.error('Failed to parse robot message:', e);
+ }
+ };
+}
+
+// Update robot connection status UI
+function updateRobotStatus(connected) {
+ if (connected) {
+ elements.robotStatus.className = 'status connected';
+ elements.robotStatus.innerHTML = ' Connected ';
+ elements.executeBtn.disabled = false;
+ } else {
+ elements.robotStatus.className = 'status disconnected';
+ elements.robotStatus.innerHTML = ' Disconnected ';
+ elements.executeBtn.disabled = true;
+ }
+}
+
+// Update compile status UI
+function updateCompileStatus(status, message) {
+ const dot = status === 'success' ? 'green' : status === 'error' ? 'red' : 'gray';
+ elements.compileStatus.className = `status ${status}`;
+ elements.compileStatus.innerHTML = ` ${message} `;
+}
+
+// Log to console
+function log(message, type = 'info') {
+ const line = document.createElement('div');
+ line.className = `console-line ${type}`;
+ line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
+ elements.console.appendChild(line);
+ elements.console.scrollTop = elements.console.scrollHeight;
+}
+
+// Clear console
+function clearConsole() {
+ elements.console.innerHTML = '';
+}
+
+// Load example script
+function loadExample(exampleName) {
+ if (examples[exampleName]) {
+ elements.editor.value = examples[exampleName];
+ log(`Loaded ${exampleName} example`, 'info');
+ }
+}
+
+// Clear editor
+function clearEditor() {
+ elements.editor.value = '';
+ state.currentIR = null;
+ elements.irDisplay.innerHTML = '
No IR yet. Verify a script first!
';
+ updateCompileStatus('idle', 'Ready');
+}
+
+// Verify script (syntax and semantics only)
+async function verifyScript() {
+ const source = elements.editor.value.trim();
+
+ if (!source) {
+ log('Editor is empty', 'warning');
+ return;
+ }
+
+ clearConsole();
+ log('Verifying script...', 'info');
+ updateCompileStatus('idle', 'Verifying...');
+
+ try {
+ const response = await fetch(`${BACKEND_URL}/api/verify`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ source })
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ log('✓ Verification successful!', 'success');
+ if (result.name) log(` Name: ${result.name}`, 'info');
+ if (result.description) log(` Description: ${result.description}`, 'info');
+
+ if (result.warnings.length > 0) {
+ result.warnings.forEach(w => {
+ log(` Warning (line ${w.line}): ${w.message}`, 'warning');
+ });
+ }
+
+ updateCompileStatus('success', 'Valid script');
+ } else {
+ log('✗ Verification failed', 'error');
+ result.errors.forEach(e => {
+ log(` Error (line ${e.line}): ${e.message}`, 'error');
+ });
+ updateCompileStatus('error', 'Invalid script');
+ }
+ } catch (error) {
+ log(`Backend error: ${error.message}`, 'error');
+ updateCompileStatus('error', 'Backend error');
+ }
+}
+
+// Compile script to IR
+async function compileScript() {
+ const source = elements.editor.value.trim();
+
+ if (!source) {
+ log('Editor is empty', 'warning');
+ return null;
+ }
+
+ log('Compiling script to IR...', 'info');
+ updateCompileStatus('idle', 'Compiling...');
+
+ try {
+ const response = await fetch(`${BACKEND_URL}/api/compile`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ source })
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ log(`✓ Compiled ${result.ir.length} actions`, 'success');
+
+ if (result.warnings.length > 0) {
+ result.warnings.forEach(w => {
+ log(` Warning (line ${w.line}): ${w.message}`, 'warning');
+ });
+ }
+
+ state.currentIR = result.ir;
+ displayIR(result.ir);
+ updateCompileStatus('success', `${result.ir.length} actions ready`);
+ return result.ir;
+ } else {
+ log('✗ Compilation failed', 'error');
+ result.errors.forEach(e => {
+ log(` Error (line ${e.line}): ${e.message}`, 'error');
+ });
+ updateCompileStatus('error', 'Compilation failed');
+ return null;
+ }
+ } catch (error) {
+ log(`Backend error: ${error.message}`, 'error');
+ updateCompileStatus('error', 'Backend error');
+ return null;
+ }
+}
+
+// Display IR in the UI
+function displayIR(ir) {
+ if (!ir || ir.length === 0) {
+ elements.irDisplay.innerHTML = 'No actions
';
+ return;
+ }
+
+ const html = ir.map((action, idx) => {
+ let details = '';
+
+ if (action.type === 'action') {
+ details = `Duration: ${action.duration}s`;
+ if (action.head_pose) details += ', Head movement';
+ if (action.antennas) details += `, Antennas: [${action.antennas.map(a => a.toFixed(2)).join(', ')}]`;
+ if (action.body_yaw !== null) details += `, Body yaw: ${action.body_yaw.toFixed(2)}`;
+ } else if (action.type === 'wait') {
+ details = `Wait ${action.duration}s`;
+ } else if (action.type === 'picture') {
+ details = 'Take picture';
+ } else if (action.type === 'sound') {
+ details = `Play "${action.sound_name}"`;
+ if (action.blocking) details += ' (blocking)';
+ if (action.loop) details += ' (loop)';
+ }
+
+ return `
+
+
${idx + 1}. ${action.type.toUpperCase()}
+
${details}
+
+ `;
+ }).join('');
+
+ elements.irDisplay.innerHTML = html;
+}
+
+// Execute compiled script on robot
+async function executeScript() {
+ if (!state.robotConnected) {
+ log('Robot not connected!', 'error');
+ return;
+ }
+
+ if (state.isExecuting) {
+ log('Already executing a script', 'warning');
+ return;
+ }
+
+ // Always compile the current editor content before executing
+ const ir = await compileScript();
+ if (!ir) {
+ log('Cannot execute - compilation failed', 'error');
+ return;
+ }
+
+ // Execute IR actions sequentially
+ state.isExecuting = true;
+ elements.executeBtn.disabled = true;
+ clearExecutionInfo();
+ logExecution('Starting execution...', 'info');
+
+ try {
+ for (let i = 0; i < ir.length; i++) {
+ const action = ir[i];
+ logExecution(`Action ${i + 1}/${ir.length}: ${action.type}`, 'info');
+
+ if (action.type === 'action') {
+ await executeIRAction(action);
+ } else if (action.type === 'wait') {
+ await sleep(action.duration * 1000);
+ } else if (action.type === 'picture') {
+ logExecution('Picture action skipped (not implemented)', 'warning');
+ } else if (action.type === 'sound') {
+ logExecution('Sound action skipped (not implemented)', 'warning');
+ }
+ }
+
+ logExecution('✓ Execution complete!', 'success');
+ } catch (error) {
+ logExecution(`✗ Execution error: ${error.message}`, 'error');
+ } finally {
+ state.isExecuting = false;
+ if (state.robotConnected) {
+ elements.executeBtn.disabled = false;
+ }
+ }
+}
+
+// Extract euler angles (roll, pitch, yaw) from 3x3 rotation matrix
+// Uses XYZ euler convention to match scipy's R.from_matrix().as_euler("xyz")
+function matrixToEulerXYZ(rotMatrix) {
+ // Extract 3x3 rotation matrix from 4x4 pose matrix if needed
+ const R = rotMatrix;
+
+ // For XYZ euler order: R = Rz(yaw) * Ry(pitch) * Rx(roll)
+ // pitch = asin(-R[2][0])
+ // roll = atan2(R[2][1], R[2][2])
+ // yaw = atan2(R[1][0], R[0][0])
+
+ let pitch = Math.asin(-R[2][0]);
+
+ // Check for gimbal lock
+ if (Math.abs(Math.cos(pitch)) > 1e-6) {
+ // Normal case
+ const roll = Math.atan2(R[2][1], R[2][2]);
+ const yaw = Math.atan2(R[1][0], R[0][0]);
+ return { roll, pitch, yaw };
+ } else {
+ // Gimbal lock case
+ const roll = Math.atan2(-R[0][1], R[1][1]);
+ const yaw = 0;
+ return { roll, pitch, yaw };
+ }
+}
+
+// Execute a single IR action (send to robot)
+async function executeIRAction(action) {
+ if (!state.robotWs || state.robotWs.readyState !== WebSocket.OPEN) {
+ throw new Error('Robot WebSocket not connected');
+ }
+
+ const message = {};
+
+ if (action.head_pose) {
+ // Extract translation from 4x4 matrix
+ const x = action.head_pose[0][3];
+ const y = action.head_pose[1][3];
+ const z = action.head_pose[2][3];
+
+ // Extract rotation (euler angles) from 3x3 rotation matrix portion
+ const euler = matrixToEulerXYZ(action.head_pose);
+
+ message.target_head_pose = {
+ x: x,
+ y: y,
+ z: z,
+ roll: euler.roll,
+ pitch: euler.pitch,
+ yaw: euler.yaw
+ };
+ }
+
+ if (action.antennas) {
+ message.target_antennas = action.antennas;
+ }
+
+ if (action.body_yaw !== null) {
+ message.target_body_yaw = action.body_yaw;
+ }
+
+ // Send to robot
+ state.robotWs.send(JSON.stringify(message));
+
+ // Wait for the action duration
+ await sleep(action.duration * 1000);
+}
+
+// Log execution info
+function logExecution(message, type = 'info') {
+ const line = document.createElement('div');
+ line.className = `console-line ${type}`;
+ line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
+ elements.executionInfo.appendChild(line);
+ elements.executionInfo.scrollTop = elements.executionInfo.scrollHeight;
+}
+
+// Clear execution info
+function clearExecutionInfo() {
+ elements.executionInfo.innerHTML = '';
+}
+
+// Sleep helper
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+// Initialize application
+function init() {
+ console.log('Initializing RMScript Web Demo');
+
+ // Connect to robot
+ connectRobotWebSocket();
+
+ // Load default example
+ loadExample('basic');
+
+ console.log('RMScript Web Demo ready');
+}
+
+// Start when DOM is loaded
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+} else {
+ init();
+}
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..c321f0f4c617a19faeaacea0a02c18dcc4ab610a
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,331 @@
+
+
+
+
+
+ RMScript Web Demo
+
+
+
+
+
+
+
🤖 RMScript Editor
+
Write and test your rmscript code
+
+
+ Ready
+
+
+
Examples
+
+ Basic
+ Complex
+ Repeat
+ Clear
+
+
+
+
+
+ ✓ Verify
+ ▶ Execute
+
+
+
Console
+
+
Ready. Write some rmscript and click Verify!
+
+
+
+
+
+
🔗 Robot Connection
+
Connect to Reachy Mini on localhost:8000
+
+
+ Disconnected
+
+
+
Compiled IR
+
+
+ No IR yet. Verify a script first!
+
+
+
+
Execution Info
+
+
Ready to execute when robot is connected
+
+
+
+
+
+
+
+
diff --git a/reachy_mini/.gitattributes b/reachy_mini/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..bf1727ba32644d4c4d06ac899b729db42b0cdcb2
--- /dev/null
+++ b/reachy_mini/.gitattributes
@@ -0,0 +1,15 @@
+*.png filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs -text
+*.jpeg filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+*.mp4 filter=lfs diff=lfs merge=lfs -text
+*.avi filter=lfs diff=lfs merge=lfs -text
+*.zip filter=lfs diff=lfs merge=lfs -text
+*.tar.gz filter=lfs diff=lfs merge=lfs -text
+*.pdf filter=lfs diff=lfs merge=lfs -text
+*.obj filter=lfs diff=lfs merge=lfs -text
+*.stl filter=lfs diff=lfs merge=lfs -text
+*.npz filter=lfs diff=lfs merge=lfs -text
+*.onnx filter=lfs diff=lfs merge=lfs -text
+*.bin filter=lfs diff=lfs merge=lfs -text
+*.svg filter=lfs diff=lfs merge=lfs -text
diff --git a/reachy_mini/.github/workflows/lint.yml b/reachy_mini/.github/workflows/lint.yml
new file mode 100644
index 0000000000000000000000000000000000000000..980524f6d7afa0d0953c75c88174ce006559d216
--- /dev/null
+++ b/reachy_mini/.github/workflows/lint.yml
@@ -0,0 +1,27 @@
+name: Linters
+on: [ push, pull_request ]
+jobs:
+ ruff:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: astral-sh/ruff-action@v3
+ with:
+ version: "0.12.0"
+
+ mypy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ cache: 'pip' # caching pip dependencies
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install .[dev]
+ - name : Lint with mypy
+ run : |
+ mypy --install-types --non-interactive
diff --git a/reachy_mini/.github/workflows/pytest.yml b/reachy_mini/.github/workflows/pytest.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1a8c75fdd271a497eb4b8fe5d259fb1aa9982e3b
--- /dev/null
+++ b/reachy_mini/.github/workflows/pytest.yml
@@ -0,0 +1,28 @@
+name: Pytest
+on: [pull_request]
+jobs:
+ pytest:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ lfs: "true"
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ cache: "pip"
+ - name: Install system dependencies (Ubuntu)
+ if: matrix.os == 'ubuntu-latest'
+ run: sudo apt-get update && sudo apt-get install -y libportaudio2
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install .[dev]
+ - name: Run tests
+ run: |
+ pytest -vv -m 'not audio and not video and not audio_gstreamer and not video_gstreamer and not wireless and not wireless_gstreamer' --tb=short
+ env:
+ MUJOCO_GL: disable
diff --git a/reachy_mini/.github/workflows/wheels.yml b/reachy_mini/.github/workflows/wheels.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dca1e95cab0816e05abd01fc2cbf6c48ae6e8f84
--- /dev/null
+++ b/reachy_mini/.github/workflows/wheels.yml
@@ -0,0 +1,26 @@
+name: Publish
+
+on:
+ release:
+ types:
+ - created
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ lfs: true
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.10"
+ - name: Install build dependencies
+ run: pip install build
+ - name: Build distribution
+ run: python -m build
+ - name: Publish
+ uses: pypa/gh-action-pypi-publish@v1.13.0
\ No newline at end of file
diff --git a/reachy_mini/.github/workflows/wheels.yml.bak b/reachy_mini/.github/workflows/wheels.yml.bak
new file mode 100644
index 0000000000000000000000000000000000000000..256d8da0e8dcfbd26880be47094a606b1e3b5813
--- /dev/null
+++ b/reachy_mini/.github/workflows/wheels.yml.bak
@@ -0,0 +1,181 @@
+name: Build and Release Wheels
+
+on:
+ push:
+ tags:
+ - "*"
+ workflow_dispatch:
+
+jobs:
+ linux:
+ runs-on: ${{ matrix.platform.runner }}
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ strategy:
+ matrix:
+ platform:
+ - runner: ubuntu-22.04
+ target: x86_64
+ - runner: ubuntu-22.04
+ target: x86
+ - runner: ubuntu-22.04
+ target: aarch64
+ - runner: ubuntu-22.04
+ target: armv7
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: |
+ 3.8
+ 3.9
+ 3.10
+ 3.11
+ 3.12
+ 3.13
+ - name: Build wheels
+ run: |
+ pip install build
+ python -m build --wheel --outdir dist
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-linux-${{ matrix.platform.target }}
+ path: dist/*.whl
+
+ musllinux:
+ runs-on: ${{ matrix.platform.runner }}
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ strategy:
+ matrix:
+ platform:
+ - runner: ubuntu-22.04
+ target: x86_64
+ - runner: ubuntu-22.04
+ target: x86
+ - runner: ubuntu-22.04
+ target: aarch64
+ - runner: ubuntu-22.04
+ target: armv7
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: |
+ 3.8
+ 3.9
+ 3.10
+ 3.11
+ 3.12
+ 3.13
+ - name: Build wheels
+ run: |
+ pip install build
+ python -m build --wheel --outdir dist
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-musllinux-${{ matrix.platform.target }}
+ path: dist/*.whl
+
+ windows:
+ runs-on: ${{ matrix.platform.runner }}
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ strategy:
+ matrix:
+ platform:
+ - runner: windows-latest
+ target: x64
+ - runner: windows-latest
+ target: x86
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: |
+ 3.8
+ 3.9
+ 3.10
+ 3.11
+ 3.12
+ 3.13
+ architecture: ${{ matrix.platform.target }}
+ - name: Build wheels
+ run: |
+ pip install build
+ python -m build --wheel --outdir dist
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-windows-${{ matrix.platform.target }}
+ path: dist/*.whl
+
+ macos:
+ runs-on: ${{ matrix.platform.runner }}
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ strategy:
+ matrix:
+ platform:
+ - runner: macos-13
+ target: x86_64
+ - runner: macos-14
+ target: aarch64
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: |
+ 3.8
+ 3.9
+ 3.10
+ 3.11
+ 3.12
+ 3.13
+ - name: Build wheels
+ run: |
+ pip install build
+ python -m build --wheel --outdir dist
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-macos-${{ matrix.platform.target }}
+ path: dist/*.whl
+
+ sdist:
+ runs-on: ubuntu-latest
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build sdist
+ run: |
+ pip install build
+ python -m build --sdist --outdir dist
+ - name: Upload sdist
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-sdist
+ path: dist/*.tar.gz
+
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
+ needs: [linux, musllinux, windows, macos, sdist]
+ permissions:
+ id-token: write
+ contents: write
+ attestations: write
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: wheels-*
+ merge-multiple: true
+ path: dist
+ - name: Generate artifact attestation
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-path: 'dist/*'
+ - name: Publish to PyPI
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ packages-dir: dist/
\ No newline at end of file
diff --git a/reachy_mini/.gitignore b/reachy_mini/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..511e897669bb76553c0e63496a2f0e323b8e3045
--- /dev/null
+++ b/reachy_mini/.gitignore
@@ -0,0 +1,256 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[codz]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py.cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+# Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+# poetry.lock
+# poetry.toml
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
+# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
+# pdm.lock
+# pdm.toml
+.pdm-python
+.pdm-build/
+
+# pixi
+# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
+# pixi.lock
+# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
+# in the .venv directory. It is recommended not to include this directory in version control.
+.pixi
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# Redis
+*.rdb
+*.aof
+*.pid
+
+# RabbitMQ
+mnesia/
+rabbitmq/
+rabbitmq-data/
+
+# ActiveMQ
+activemq-data/
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.envrc
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+.idea/**/contentModel.xml
+
+# Abstra
+# Abstra is an AI-powered process automation framework.
+# Ignore directories containing user credentials, local state, and settings.
+# Learn more at https://abstra.io/docs
+.abstra/
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+!*.code-workspace
+
+# Ruff stuff:
+.ruff_cache/
+
+# PyPI configuration file
+.pypirc
+
+# Marimo
+marimo/_static/
+marimo/_lsp/
+__marimo__/
+
+# Streamlit
+.streamlit/secrets.toml
+
+# General for macOS
+.DS_Store
+__MACOSX/
+.AppleDouble
+.LSOverride
+Icon[]
+
+# General for Linux
+*~
+.fuse_hidden*
+.directory
+.Trash-*
+.nfs*
+nohup.out
+
+# General for Windows
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+[Dd]esktop.ini
+*.stackdump
+$RECYCLE.BIN/
+*.lnk
+
+# Project specific ignores
+src/reachy_mini_dashboard/installed_apps/*
+examples/debug/measures/*
+.DS_Store
\ No newline at end of file
diff --git a/reachy_mini/.pre-commit-config.yaml b/reachy_mini/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..273d3502025f81c79f18c30971e9aaa6f0e45cee
--- /dev/null
+++ b/reachy_mini/.pre-commit-config.yaml
@@ -0,0 +1,10 @@
+repos:
+- repo: https://github.com/astral-sh/ruff-pre-commit
+ # Ruff version. Make sure it matches the version in .github/workflows/lint.yml.
+ rev: v0.12.0
+ hooks:
+ # Run the linter.
+ - id: ruff-check
+ args: ["--select", "I", "--select", "D", "--ignore", "D203", "--ignore", "D213"]
+ # Run the formatter.
+ - id: ruff-format
\ No newline at end of file
diff --git a/reachy_mini/LICENSE b/reachy_mini/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..2bae156ba764607d491a34535fb0fb5a02022793
--- /dev/null
+++ b/reachy_mini/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 Pollen Robotics
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/reachy_mini/MANIFEST.in b/reachy_mini/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..f51342cf3346a74203c815f86556947c51e99f8a
--- /dev/null
+++ b/reachy_mini/MANIFEST.in
@@ -0,0 +1,3 @@
+recursive-include src/reachy_mini/assets *
+recursive-include src/reachy_mini/templates *
+recursive-include src/reachy_mini/descriptions *
diff --git a/reachy_mini/README.md b/reachy_mini/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e2079c0905d627a54a0424e5d0ea1a34ff9c2f79
--- /dev/null
+++ b/reachy_mini/README.md
@@ -0,0 +1,246 @@
+# Reachy Mini
+
+> ⚠️ Reachy Mini is still in beta. Expect bugs, some of them we won't fix right away if they are not a priority.
+
+[Reachy Mini](https://www.pollen-robotics.com/reachy-mini/) is an expressive, open-source robot designed for human-robot interaction, creative coding, and AI experimentation. We made it to be affordable, easy to use, hackable and cute, so that you can focus on building cool AI applications!
+
+[](https://www.pollen-robotics.com/reachy-mini/)
+
+### Versions Lite & Wireless
+
+Reachy Mini's hardware comes in two flavors:
+- **Reachy Mini lite**: where the robot is directly connected to your computer via USB. And the code that controls the robot (the daemon) runs on your computer.
+- **Reachy Mini wireless**: where an Raspberry Pi is embedded in the robot, and the code that controls the robot (the daemon) runs on the Raspberry Pi. You can connect to it via Wi-Fi from your computer (see [Wireless Setup](./docs/wireless-version.md)).
+
+There is also a simulated version of Reachy Mini in [MuJoCo](https://mujoco.org) that you can use to prototype your applications before deploying them on the real robot. It behaves like the lite version where the daemon runs on your computer.
+
+## Assembly guide
+
+📖 Follow our step-by-step [Assembly Guide](https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Assembly_Guide).
+
+Most builders finish in about 3 hours, our current speed record is 43 minutes. The guide walks you through every step with clear visuals so you can assemble Reachy Mini confidently from start to finish. Enjoy the build!
+
+▶️ View the [Assembly Video](https://youtu.be/_r0cHySFbeY?si=6Mw4js8HSUs4cwoJ).
+
+## Software overview
+
+This repository provides everything you need to control Reachy Mini, both in simulation and on the real robot. It consists of two main parts:
+
+- **The 😈 Daemon 😈**: A background service that manages communication with the robot's motors and sensors, or with the simulation environment. It should be running before you can control the robot. It can run either for the simulation (MuJoCo) or for the real robot.
+- **🐍 SDK & 🕸️ API** to control the robot's main features (head, antennas, camera, speakers, microphone, etc.) and connect with your AI experimentation. Depending on your preferences and needs, there is a [Python SDK](#using-the-python-sdk) and a [HTTP REST API](#using-the-rest-api).
+
+Using the [Python SDK](#using-the-python-sdk), making your robot move only require a few lines of code, as illustrated in the example below:
+
+```python
+from reachy_mini import ReachyMini
+from reachy_mini.utils import create_head_pose
+
+with ReachyMini() as reachy_mini:
+ # Move the head up (10mm on z-axis) and roll it 15 degrees
+ pose = create_head_pose(z=10, roll=15, degrees=True, mm=True)
+ reachy_mini.goto_target(head=pose, duration=2.0)
+
+ # Reset to default pose
+ pose = create_head_pose()
+ reachy_mini.goto_target(head=pose, duration=2.0)
+```
+
+and using the [REST API](#using-the-rest-api), reading the current state of the robot:
+
+```bash
+curl 'http://localhost:8000/api/state/full'
+```
+
+Those two examples above assume that the daemon is already running (either in simulation or on the real robot) locally.
+
+## Installation of the daemon and Python SDK
+
+As mentioned above, before being able to use the robot, you need to run the daemon that will handle the communication with the motors.
+
+We support and test on Linux and macOS. It's also working on Windows, but it is less tested at the moment. Do not hesitate to open an issue if you encounter any problem.
+
+The daemon is built in Python, so you need to have Python installed on your computer (versions from 3.10 to 3.13 are supported). We recommend using a virtual environment to avoid dependency conflicts with your other Python projects.
+
+You can install Reachy Mini from the source code or from PyPI.
+
+First, make sure `git-lfs` is installed on your system:
+
+- On Linux: `sudo apt install git-lfs`
+- On macOS: `brew install git-lfs`
+- On Windows: [Follow the instructions here](https://docs.github.com/en/repositories/working-with-files/managing-large-files/installing-git-large-file-storage?platform=windows)
+
+From PyPI, you can install the package with:
+
+```bash
+pip install reachy-mini
+```
+
+From the source code, you can install the package with:
+
+```bash
+git clone https://github.com/pollen-robotics/reachy_mini
+pip install -e ./reachy_mini
+```
+
+*Note that uv users can directly run the daemon with:*
+```bash
+uv run reachy-mini-daemon
+```
+
+The same package provides both the daemon and the Python SDK.
+
+
+## Run the reachy mini daemon
+
+Before being able to use the robot, you need to run the daemon that will handle the communication with the motors. This daemon can run either in simulation (MuJoCo) or on the real robot.
+
+```bash
+reachy-mini-daemon
+```
+
+or run it via the Python module:
+
+```bash
+python -m reachy_mini.daemon.app.main
+```
+
+Additional argument for both simulation and real robot:
+
+```bash
+--localhost-only: (default behavior). The server will only accept connections from localhost.
+```
+
+or
+
+```bash
+--no-localhost-only: If set, the server will accept connections from any connection on the local network.
+```
+
+### In simulation ([MuJoCo](https://mujoco.org))
+
+You first have to install the optional dependency `mujoco`.
+
+```bash
+pip install reachy-mini[mujoco]
+```
+
+Then run the daemon with the `--sim` argument.
+
+```bash
+reachy-mini-daemon --sim
+```
+
+Additional arguments:
+
+```bash
+--scene : (Default empty). Choose between a basic empty scene, or a scene with a table and some objects.
+```
+
+
+
+
+*Note: On OSX in order to run mujoco, you need to use mjpython (see [here](https://mujoco.readthedocs.io/en/stable/python.html#passive-viewer)). So, you should run the daemon with:*
+
+```bash
+ mjpython -m reachy_mini.daemon.app.main --sim
+ ```
+
+### For the lite version (connected via USB)
+
+It should automatically detect the serial port of the robot. If it does not, you can specify it manually with the `-p` option:
+
+```bash
+reachy-mini-daemon -p
+```
+
+### Usage
+
+For more information about the daemon and its options, you can run:
+
+```bash
+reachy-mini-daemon --help
+```
+
+### Dashboard
+
+You can access a simple dashboard to monitor the robot's status at [http://localhost:8000/](http://localhost:8000/) when the daemon is running. This lets you turn your robot on and off, run some basic movements, and browse spaces for Reachy Mini!
+
+
+
+## Run the demo & awesome apps
+
+Conversational demo for the Reachy Mini robot combining LLM realtime APIs, vision pipelines, and choreographed motion libraries: [reachy_mini_conversation_demo](https://github.com/pollen-robotics/reachy_mini_conversation_demo).
+
+You can find more awesome apps and demos for Reachy Mini on [Hugging Face spaces](https://huggingface.co/spaces?q=reachy_mini)!
+
+## Using the Python SDK
+
+The API is designed to be simple and intuitive. You can control the robot's features such as the head, antennas, camera, speakers, and microphone. For instance, to move the head of the robot, you can use the `goto_target` method as shown in the example below:
+
+```python
+from reachy_mini import ReachyMini
+from reachy_mini.utils import create_head_pose
+
+with ReachyMini() as reachy_mini:
+ # Move the head up (10mm on z-axis) and roll it 15 degrees
+ pose = create_head_pose(z=10, roll=15, degrees=True, mm=True)
+ reachy_mini.goto_target(head=pose, duration=2.0)
+
+ # Reset to default pose
+ pose = create_head_pose()
+ reachy_mini.goto_target(head=pose, duration=2.0)
+```
+
+For a full description of the SDK, please refer to the [Python SDK documentation](./docs/python-sdk.md).
+
+## Using the REST API
+
+The daemon also provides a REST API via [fastapi](https://fastapi.tiangolo.com/) that you can use to control the robot and get its state. The API is accessible via HTTP and WebSocket.
+
+By default, the API server runs on `http://localhost:8000`. The API is documented using OpenAPI, and you can access the documentation at `http://localhost:8000/docs` when the daemon is running.
+
+More information about the API can be found in the [HTTP API documentation](./docs/rest-api.md).
+
+## Open source & contribution
+
+This project is actively developed and maintained by the [Pollen Robotics team](https://www.pollen-robotics.com) and the [Hugging Face team](https://huggingface.co/).
+
+We welcome contributions from the community! If you want to report a bug or request a feature, please open an issue on GitHub. If you want to contribute code, please fork the repository and submit a pull request.
+
+### 3D models
+
+TODO
+
+### Contributing
+
+Development tools are available in the optional dependencies.
+
+```bash
+pip install -e .[dev]
+pre-commit install
+```
+
+Your files will be checked before any commit. Checks may also be manually run with
+
+```bash
+pre-commit run --all-files
+```
+
+Checks are performed by Ruff. You may want to [configure your IDE to support it](https://docs.astral.sh/ruff/editors/setup/).
+
+## Troubleshooting
+
+see [dedicated section](docs/troubleshooting.md)
+
+## License
+
+This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
+
+The robot design files are licensed under the [TODO](TODO) license.
+
+### Simulation model used
+
+- https://polyhaven.com/a/food_apple_01
+- https://polyhaven.com/a/croissant
+- https://polyhaven.com/a/wooden_table_02
+- https://polyhaven.com/a/rubber_duck_toy
diff --git a/reachy_mini/conftest.py b/reachy_mini/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/reachy_mini/pyproject.toml b/reachy_mini/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f243ea1aa781f040f4efe15558f218acec865699
--- /dev/null
+++ b/reachy_mini/pyproject.toml
@@ -0,0 +1,111 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "reachy_mini"
+version = "1.1.1"
+authors = [{ name = "Pollen Robotics", email = "contact@pollen-robotics.com" }]
+description = ""
+readme = "README.md"
+requires-python = ">=3.10"
+dependencies = [
+ "numpy>=2.2.5",
+ "scipy>=1.15.3, <2.0.0",
+ "reachy_mini_motor_controller>=1.4.2",
+ "eclipse-zenoh>=1.4.0",
+ "opencv-python<=5.0",
+ "cv2_enumerate_cameras>=1.2.1",
+ "psutil",
+ "jinja2",
+ "uvicorn[standard]",
+ "fastapi",
+ "pyserial",
+ "huggingface-hub==0.34.4",
+ "sounddevice==0.5.1",
+ "soundfile==0.13.1",
+ "reachy-mini-rust-kinematics>=1.0.2",
+ "asgiref",
+ "aiohttp",
+ "log-throttling==0.0.3",
+ "pyusb>=1.2.1",
+ "libusb_package>=1.0.26.3",
+ "pip>=25",
+]
+
+
+[project.optional-dependencies]
+dev = [
+ "pytest",
+ "pytest-asyncio",
+ "ruff==0.12.0",
+ "onshape-to-robot==1.7.6",
+ "mujoco==3.3.0",
+ "placo==0.9.14",
+ "pre-commit",
+ "mypy==1.18.2",
+ "rustypot>=1.4.0",
+ "types-requests",
+ "rerun-sdk>=0.27.2",
+ "urdf-parser-py==0.0.4",
+]
+examples = ["pynput"]
+mujoco = ["mujoco==3.3.0"]
+nn_kinematics = ["onnxruntime==1.22.1"]
+placo_kinematics = ["placo==0.9.14"]
+gstreamer = ["PyGObject>=3.42.2,<=3.46.0", "gst-signalling>=1.1.2"]
+rerun = ["rerun-sdk>=0.27.2", "urdf-parser-py==0.0.4"]
+wireless-version = [
+ "semver>=3,<4",
+ "nmcli>=1.5",
+ "pollen_BMI088_imu_library",
+ "gpiozero>=2.0.0",
+ "lgpio>=0.2.2.0",
+]
+
+[project.scripts]
+reachy-mini-daemon = "reachy_mini.daemon.app.main:main"
+reachy-mini-apps-helper = "reachy_mini.apps.app:main"
+
+[tool.setuptools]
+package-dir = { "" = "src" }
+include-package-data = true
+
+
+[tool.setuptools.packages.find]
+where = ["src"]
+
+
+[tool.setuptools.package-data]
+reachy_mini = ["**/*"] # Inclut tous les fichiers non .py
+
+[tool.ruff]
+exclude = [
+ "src/reachy_mini/__init__.py",
+ "build/*",
+ "conftest.py",
+ "src/reachy_mini_dashboard/*",
+ "tests/*",
+]
+lint.extend-select = ["I", "D"]
+lint.ignore = [
+ "D203", # Incompatible with D211
+ "D213", # Incompatible with D212
+]
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+markers = [
+ "audio: mark test as requiring audio hardware",
+ "audio_gstreamer: mark test as requiring GStreamer for audio",
+ "video: mark test as requiring video hardware",
+ "video_gstreamer: mark test as requiring GStreamer for video",
+ "wireless: mark test as requiring wireless Reachy Mini",
+ "wireless_gstreamer: mark test as requiring GStreamer for wireless Reachy Mini",
+]
+
+[tool.mypy]
+python_version = "3.10"
+files = ["src/"]
+ignore_missing_imports = true
+strict = true
diff --git a/reachy_mini/src/reachy_mini/__init__.py b/reachy_mini/src/reachy_mini/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dc26157e7280df3751f8fa238af92dff3a06f66
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/__init__.py
@@ -0,0 +1,4 @@
+"""Reachy Mini SDK."""
+
+from reachy_mini.apps.app import ReachyMiniApp # noqa: F401
+from reachy_mini.reachy_mini import ReachyMini # noqa: F401
diff --git a/reachy_mini/src/reachy_mini/apps/__init__.py b/reachy_mini/src/reachy_mini/apps/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..94c3e5fcf6b4b7e5c913b171674d681665867860
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/__init__.py
@@ -0,0 +1,25 @@
+"""Metadata about apps."""
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Any, Dict
+
+
+class SourceKind(str, Enum):
+ """Kinds of app source."""
+
+ HF_SPACE = "hf_space"
+ DASHBOARD_SELECTION = "dashboard_selection"
+ LOCAL = "local"
+ INSTALLED = "installed"
+
+
+@dataclass
+class AppInfo:
+ """Metadata about an app."""
+
+ name: str
+ source_kind: SourceKind
+ description: str = ""
+ url: str | None = None
+ extra: Dict[str, Any] = field(default_factory=dict)
diff --git a/reachy_mini/src/reachy_mini/apps/app.py b/reachy_mini/src/reachy_mini/apps/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..552871785b776b161bdcff74691ce880c3f9bb5f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/app.py
@@ -0,0 +1,169 @@
+"""Reachy Mini Application Base Class.
+
+This module provides a base class for creating Reachy Mini applications.
+It includes methods for running the application, stopping it gracefully,
+and creating a new app project with a specified name and path.
+
+It uses Jinja2 templates to generate the necessary files for the app project.
+"""
+
+import os
+import threading
+import traceback
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import Any, Dict
+
+from jinja2 import Environment, FileSystemLoader
+
+from reachy_mini.reachy_mini import ReachyMini
+
+
+class ReachyMiniApp(ABC):
+ """Base class for Reachy Mini applications."""
+
+ custom_app_url: str | None = None
+
+ def __init__(self) -> None:
+ """Initialize the Reachy Mini app."""
+ self.stop_event = threading.Event()
+ self.error: str = ""
+
+ def wrapped_run(self, *args: Any, **kwargs: Any) -> None:
+ """Wrap the run method with Reachy Mini context management."""
+ try:
+ with ReachyMini(*args, **kwargs) as reachy_mini:
+ self.run(reachy_mini, self.stop_event)
+ except Exception:
+ self.error = traceback.format_exc()
+ raise
+
+ @abstractmethod
+ def run(self, reachy_mini: ReachyMini, stop_event: threading.Event) -> None:
+ """Run the main logic of the app.
+
+ Args:
+ reachy_mini (ReachyMini): The Reachy Mini instance to interact with.
+ stop_event (threading.Event): An event that can be set to stop the app gracefully.
+
+ """
+ pass
+
+ def stop(self) -> None:
+ """Stop the app gracefully."""
+ self.stop_event.set()
+ print("App is stopping...")
+
+
+def make_app_project(app_name: str, path: Path) -> None:
+ """Create a new Reachy Mini app project with the given name at the specified path.
+
+ Args:
+ app_name (str): The name of the app to create.
+ path (Path): The directory where the app project will be created.
+
+ """
+ TEMPLATE_DIR = Path(__file__).parent / "templates"
+ env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
+
+ def render_template(filename: str, context: Dict[str, str]) -> str:
+ template = env.get_template(filename)
+ return template.render(context)
+
+ base_path = Path(path).resolve() / app_name
+ if base_path.exists():
+ print(f"❌ Folder {base_path} already exists.")
+ return
+
+ module_name = app_name.replace("-", "_")
+ class_name = "".join(word.capitalize() for word in module_name.split("_"))
+ class_name_display = " ".join(word.capitalize() for word in module_name.split("_"))
+
+ base_path.mkdir()
+ (base_path / module_name).mkdir()
+
+ # Generate files
+ context = {
+ "app_name": app_name,
+ "package_name": app_name,
+ "module_name": module_name,
+ "class_name": class_name,
+ "class_name_display": class_name_display,
+ }
+
+ (base_path / module_name / "__init__.py").touch()
+ (base_path / module_name / "main.py").write_text(
+ render_template("main.py.j2", context)
+ )
+ (base_path / "pyproject.toml").write_text(
+ render_template("pyproject.toml.j2", context)
+ )
+ (base_path / "README.md").write_text(render_template("README.md.j2", context))
+
+ (base_path / "index.html").write_text(render_template("index.html.j2", context))
+ (base_path / "style.css").write_text(render_template("style.css.j2", context))
+
+ print(f"✅ Created app in {base_path}/")
+
+
+def publish_app(app_path: str, hf_space_url: Path) -> None:
+ """Publish the app to the Reachy Mini app store.
+
+ Args:
+ app_path (str): Local path to the app to publish.
+ hf_space_url (Path): Url to the space on Hugging Face.
+
+ """
+ # Placeholder for publishing logic
+ assert os.path.exists(app_path), f"App path {app_path} does not exist."
+ print(f"Publishing app from {app_path} to {hf_space_url}...")
+ os.system(
+ f"cd {app_path} && git init && git remote add space {hf_space_url} && git add . && git commit -m 'Initial commit' && git push -f space main:main"
+ )
+ print("✅ App published successfully.")
+
+
+def main() -> None:
+ """Run the command line interface to create a new Reachy Mini app project."""
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ description="Create a new Reachy Mini app project."
+ )
+ subparsers = parser.add_subparsers(
+ dest="command", help="Available commands", required=True
+ )
+
+ make_parser = subparsers.add_parser("make", help="Make a new app project")
+ make_parser.add_argument(
+ "--option", type=str, help="An option for the make command"
+ )
+ make_parser.add_argument("app_name", type=str, help="Name of the app to create.")
+ make_parser.add_argument(
+ "path",
+ type=Path,
+ default=Path.cwd(),
+ help="Path where the app project will be created.",
+ )
+
+ publish_parser = subparsers.add_parser(
+ "publish", help="Publish the app to the Reachy Mini app store"
+ )
+ publish_parser.add_argument(
+ "app_path", type=str, help="Local path to the app to publish."
+ )
+ publish_parser.add_argument(
+ "hf_space_url", type=Path, help="Url to the space on Hugging Face."
+ )
+
+ args = parser.parse_args()
+
+ if args.command == "make":
+ make_app_project(args.app_name, args.path)
+
+ elif args.command == "publish":
+ publish_app(args.app_path, args.hf_space_url)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/src/reachy_mini/apps/manager.py b/reachy_mini/src/reachy_mini/apps/manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..070c857d349f65db7f7c1637f9a35016ac1aa3b4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/manager.py
@@ -0,0 +1,185 @@
+"""App management for Reachy Mini."""
+
+import asyncio
+import logging
+from dataclasses import dataclass
+from enum import Enum
+from importlib.metadata import entry_points
+from threading import Thread
+from typing import TYPE_CHECKING, Any, Optional
+
+from pydantic import BaseModel
+
+from . import AppInfo, SourceKind
+from .sources import hf_space, local_common_venv
+
+if TYPE_CHECKING:
+ from .app import ReachyMiniApp
+
+
+class AppState(str, Enum):
+ """Status of a running app."""
+
+ STARTING = "starting"
+ RUNNING = "running"
+ DONE = "done"
+ STOPPING = "stopping"
+ ERROR = "error"
+
+
+class AppStatus(BaseModel):
+ """Status of an app."""
+
+ info: AppInfo
+ state: AppState
+ error: str | None = None
+
+
+@dataclass
+class RunningApp:
+ """Information about a running app."""
+
+ app: "ReachyMiniApp"
+ thread: Thread
+ status: AppStatus
+
+
+class AppManager:
+ """Manager for Reachy Mini apps."""
+
+ def __init__(self) -> None:
+ """Initialize the AppManager."""
+ self.current_app = None # type: RunningApp | None
+ self.logger = logging.getLogger("reachy_mini.apps.manager")
+
+ async def close(self) -> None:
+ """Clean up the AppManager, stopping any running app."""
+ if self.is_app_running():
+ await self.stop_current_app()
+
+ # App lifecycle management
+ # Only one app can be started at a time for now
+ def is_app_running(self) -> bool:
+ """Check if an app is currently running."""
+ return self.current_app is not None and self.current_app.status.state in (
+ AppState.STARTING,
+ AppState.RUNNING,
+ AppState.ERROR,
+ )
+
+ async def start_app(self, app_name: str, *args: Any, **kwargs: Any) -> AppStatus:
+ """Start the app, raises RuntimeError if an app is already running."""
+ if self.is_app_running():
+ raise RuntimeError("An app is already running")
+
+ (ep,) = entry_points(group="reachy_mini_apps", name=app_name)
+ app = ep.load()()
+
+ def wrapped_run() -> None:
+ assert self.current_app is not None
+
+ try:
+ self.current_app.status.state = AppState.RUNNING
+ self.logger.getChild("runner").info(f"App {app_name} is running")
+ app.wrapped_run(*args, **kwargs)
+ self.current_app.status.state = AppState.DONE
+ self.logger.getChild("runner").info(f"App {app_name} finished")
+ except Exception as e:
+ self.logger.getChild("runner").error(
+ f"An error occurred in the app {app_name}: {e}"
+ )
+ self.logger.getChild("runner").error(
+ f"Exception details: '{app.error}'",
+ )
+ self.current_app.status.state = AppState.ERROR
+ self.current_app.status.error = str(app.error)
+
+ self.current_app = RunningApp(
+ status=AppStatus(
+ info=AppInfo(name=app_name, source_kind=SourceKind.INSTALLED),
+ state=AppState.STARTING,
+ error=None,
+ ),
+ app=app,
+ thread=Thread(target=wrapped_run),
+ )
+ self.logger.getChild("runner").info(f"Starting app {app_name}")
+ self.current_app.thread.start()
+
+ return self.current_app.status
+
+ async def stop_current_app(self, timeout: float | None = 5.0) -> None:
+ """Stop the current app."""
+ if not self.is_app_running():
+ raise RuntimeError("No app is currently running")
+
+ assert self.current_app is not None
+
+ self.current_app.status.state = AppState.STOPPING
+ self.logger.getChild("runner").info(
+ f"Stopping app {self.current_app.status.info.name}"
+ )
+ self.current_app.app.stop()
+ self.current_app.thread.join(timeout)
+
+ if self.current_app.thread.is_alive():
+ self.logger.getChild("runner").warning(
+ "The app did not stop within the timeout"
+ )
+ else:
+ self.logger.getChild("runner").info("App stopped successfully")
+
+ self.current_app = None
+
+ async def restart_current_app(self) -> AppStatus:
+ """Restart the current app."""
+ if not self.is_app_running():
+ raise RuntimeError("No app is currently running")
+
+ assert self.current_app is not None
+
+ app_info = self.current_app.status.info
+
+ await self.stop_current_app()
+ await self.start_app(app_info.name)
+
+ return self.current_app.status
+
+ async def current_app_status(self) -> Optional[AppStatus]:
+ """Get the current status of the app."""
+ if self.current_app is not None:
+ return self.current_app.status
+ return None
+
+ # Apps management interface
+ async def list_all_available_apps(self) -> list[AppInfo]:
+ """List available apps (parallel async)."""
+ results = await asyncio.gather(
+ *[self.list_available_apps(kind) for kind in SourceKind]
+ )
+ return sum(results, [])
+
+ async def list_available_apps(self, source: SourceKind) -> list[AppInfo]:
+ """List available apps for given source kind."""
+ if source == SourceKind.HF_SPACE:
+ return await hf_space.list_all_apps()
+ elif source == SourceKind.DASHBOARD_SELECTION:
+ return await hf_space.list_available_apps()
+ elif source == SourceKind.INSTALLED:
+ return await local_common_venv.list_available_apps()
+ elif source == SourceKind.LOCAL:
+ return []
+ else:
+ raise NotImplementedError(f"Unknown source kind: {source}")
+
+ async def install_new_app(self, app: AppInfo, logger: logging.Logger) -> None:
+ """Install a new app by name."""
+ success = await local_common_venv.install_package(app, logger)
+ if success != 0:
+ raise RuntimeError(f"Failed to install app '{app.name}'")
+
+ async def remove_app(self, app_name: str, logger: logging.Logger) -> None:
+ """Remove an installed app by name."""
+ success = await local_common_venv.uninstall_package(app_name, logger)
+ if success != 0:
+ raise RuntimeError(f"Failed to uninstall app '{app_name}'")
diff --git a/reachy_mini/src/reachy_mini/apps/sources/__init__.py b/reachy_mini/src/reachy_mini/apps/sources/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c83411aac64dca6fe6597f174ab5e7f22ab5f16
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/sources/__init__.py
@@ -0,0 +1,4 @@
+"""Specific app sources.
+
+For the moment, only huggingface spaces is implemented.
+"""
diff --git a/reachy_mini/src/reachy_mini/apps/sources/hf_space.py b/reachy_mini/src/reachy_mini/apps/sources/hf_space.py
new file mode 100644
index 0000000000000000000000000000000000000000..23dc5d2752168487bacfe0cc5ceb7433c5b6d9e7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/sources/hf_space.py
@@ -0,0 +1,105 @@
+"""Hugging Face Spaces app source."""
+
+import asyncio
+import json
+from typing import Any, Dict
+
+import aiohttp
+
+from .. import AppInfo, SourceKind
+
+# Constants
+AUTHORIZED_APP_LIST_URL = "https://huggingface.co/datasets/pollen-robotics/reachy-mini-official-app-store/raw/main/app-list.json"
+HF_SPACES_API_URL = "https://huggingface.co/api/spaces"
+HF_SPACES_FILTER_URL = "https://huggingface.co/api/spaces?filter=reachy_mini&sort=likes&direction=-1&limit=50&full=true"
+REQUEST_TIMEOUT = aiohttp.ClientTimeout(total=30)
+
+
+async def _fetch_space_data(
+ session: aiohttp.ClientSession, space_id: str
+) -> Dict[str, Any] | None:
+ """Fetch data for a single space from Hugging Face API."""
+ url = f"{HF_SPACES_API_URL}/{space_id}"
+ try:
+ async with session.get(url, timeout=REQUEST_TIMEOUT) as response:
+ if response.status == 200:
+ data: Dict[str, Any] = await response.json()
+ return data
+ else:
+ return None
+ except (aiohttp.ClientError, asyncio.TimeoutError):
+ return None
+
+
+async def list_available_apps() -> list[AppInfo]:
+ """List apps available on Hugging Face Spaces."""
+ async with aiohttp.ClientSession(timeout=REQUEST_TIMEOUT) as session:
+ # Fetch the list of authorized app IDs
+ try:
+ async with session.get(AUTHORIZED_APP_LIST_URL) as response:
+ response.raise_for_status()
+ text = await response.text()
+ authorized_ids = json.loads(text)
+ except (aiohttp.ClientError, json.JSONDecodeError):
+ return []
+
+ if not isinstance(authorized_ids, list):
+ return []
+
+ # Filter to only string elements
+ authorized_ids = [
+ space_id for space_id in authorized_ids if isinstance(space_id, str)
+ ]
+
+ # Fetch data for each space in parallel
+ tasks = [_fetch_space_data(session, space_id) for space_id in authorized_ids]
+ spaces_data = await asyncio.gather(*tasks)
+
+ # Build AppInfo list from fetched data
+ apps = []
+ for item in spaces_data:
+ if item is None or "id" not in item:
+ continue
+
+ apps.append(
+ AppInfo(
+ name=item["id"].split("/")[-1],
+ description=item.get("cardData", {}).get("short_description", ""),
+ url=f"https://huggingface.co/spaces/{item['id']}",
+ source_kind=SourceKind.HF_SPACE,
+ extra=item,
+ )
+ )
+
+ return apps
+
+
+async def list_all_apps() -> list[AppInfo]:
+ """List all apps available on Hugging Face Spaces (including unofficial ones)."""
+ async with aiohttp.ClientSession(timeout=REQUEST_TIMEOUT) as session:
+ try:
+ async with session.get(HF_SPACES_FILTER_URL) as response:
+ response.raise_for_status()
+ data: list[Dict[str, Any]] = await response.json()
+ except (aiohttp.ClientError, json.JSONDecodeError, asyncio.TimeoutError):
+ return []
+
+ if not isinstance(data, list):
+ return []
+
+ apps = []
+ for item in data:
+ if item is None or "id" not in item:
+ continue
+
+ apps.append(
+ AppInfo(
+ name=item["id"].split("/")[-1],
+ description=item.get("cardData", {}).get("short_description", ""),
+ url=f"https://huggingface.co/spaces/{item['id']}",
+ source_kind=SourceKind.HF_SPACE,
+ extra=item,
+ )
+ )
+
+ return apps
diff --git a/reachy_mini/src/reachy_mini/apps/sources/local_common_venv.py b/reachy_mini/src/reachy_mini/apps/sources/local_common_venv.py
new file mode 100644
index 0000000000000000000000000000000000000000..9100adb1efbe7c49c5fecace50cda77be126d217
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/sources/local_common_venv.py
@@ -0,0 +1,61 @@
+"""Utilities for local common venv apps source."""
+
+import logging
+import sys
+from importlib.metadata import entry_points
+
+from .. import AppInfo, SourceKind
+from ..utils import running_command
+
+
+async def list_available_apps() -> list[AppInfo]:
+ """List apps available from entry points."""
+ entry_point_apps = list(entry_points(group="reachy_mini_apps"))
+
+ apps = []
+
+ for ep in entry_point_apps:
+ custom_app_url = None
+ try:
+ app = ep.load()
+ custom_app_url = app.custom_app_url
+ except Exception as e:
+ logging.getLogger("reachy_mini.apps").warning(
+ f"Could not load app '{ep.name}' from entry point: {e}"
+ )
+ apps.append(
+ AppInfo(
+ name=ep.name,
+ source_kind=SourceKind.INSTALLED,
+ extra={"custom_app_url": custom_app_url},
+ )
+ )
+
+ return apps
+
+
+async def install_package(app: AppInfo, logger: logging.Logger) -> int:
+ """Install a package given an AppInfo object, streaming logs."""
+ if app.source_kind == SourceKind.HF_SPACE:
+ target = f"git+{app.url}" if app.url is not None else app.name
+ elif app.source_kind == SourceKind.LOCAL:
+ target = app.extra.get("path", app.name)
+ else:
+ raise ValueError(f"Cannot install app from source kind '{app.source_kind}'")
+
+ return await running_command(
+ [sys.executable, "-m", "pip", "install", target],
+ logger=logger,
+ )
+
+
+async def uninstall_package(app_name: str, logger: logging.Logger) -> int:
+ """Uninstall a package given an app name."""
+ existing_apps = await list_available_apps()
+ if app_name not in [app.name for app in existing_apps]:
+ raise ValueError(f"Cannot uninstall app '{app_name}': it is not installed")
+
+ return await running_command(
+ [sys.executable, "-m", "pip", "uninstall", "-y", app_name],
+ logger=logger,
+ )
diff --git a/reachy_mini/src/reachy_mini/apps/templates/README.md.j2 b/reachy_mini/src/reachy_mini/apps/templates/README.md.j2
new file mode 100644
index 0000000000000000000000000000000000000000..965c2474d5b21abcca0606a3abc401bb2a310a89
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/templates/README.md.j2
@@ -0,0 +1,11 @@
+---
+title: {{ class_name_display }}
+emoji: 👋
+colorFrom: red
+colorTo: blue
+sdk: static
+pinned: false
+short_description: Write your description here
+tags:
+ - reachy_mini
+---
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/apps/templates/index.html.j2 b/reachy_mini/src/reachy_mini/apps/templates/index.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2f0ce85c63c071b48a36f3d7234f3743b3084413
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/templates/index.html.j2
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+ {{ class_name_display }}
+
+
+
+
+
+
+
🤖⚡
+
{{ class_name_display }}
+
Enter your tagline here
+
+
+
+
+
+
+
+
Install This App
+
+
+ Your Reachy Dashboard URL:
+
+
+
+
+ 📥
+ Install {{ class_name_display }} to Reachy Mini
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/apps/templates/main.py.j2 b/reachy_mini/src/reachy_mini/apps/templates/main.py.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2db6dd065981435fbe9d07f23b23ac7edf7f2e8f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/templates/main.py.j2
@@ -0,0 +1,50 @@
+import threading
+
+from reachy_mini import ReachyMini, ReachyMiniApp
+from reachy_mini.utils import create_head_pose
+
+
+class {{ class_name }}(ReachyMiniApp):
+ # Optional: URL to a custom configuration page for the app
+ # eg. "http://localhost:8042/settings"
+ custom_app_url: str | None = None
+
+ def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
+ # Write your code here
+ # ReachyMini is already initialized and connected
+ # Check the stop_event to gracefully exit the loop
+ # Example:
+ # import time
+ # import numpy as np
+ #
+ # t0 = time.time()
+ #
+ # while not stop_event.is_set():
+ # t = time.time() - t0
+ #
+ # yaw = 30 * np.sin(2 * np.pi * 0.5 * t)
+ # head_pose = create_head_pose(yaw=yaw, degrees=True)
+ #
+ # reachy_mini.set_target(head=head_pose)
+ #
+ # time.sleep(0.01)
+
+ print("This is a placeholder for your app logic.")
+
+
+if __name__ == "__main__":
+ # You can run the app directly from this script
+ with ReachyMini() as mini:
+ app = {{ class_name }}()
+
+ stop = threading.Event()
+
+ try:
+ print("Running '{{ app_name }}' a ReachyMiniApp...")
+ print("Press Ctrl+C to stop the app.")
+ app.run(mini, stop)
+ print("App has stopped.")
+
+ except KeyboardInterrupt:
+ print("Stopping the app...")
+ stop.set()
diff --git a/reachy_mini/src/reachy_mini/apps/templates/pyproject.toml.j2 b/reachy_mini/src/reachy_mini/apps/templates/pyproject.toml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..196f71436980b01e5c8f4aad537ea02b87cf4e66
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/templates/pyproject.toml.j2
@@ -0,0 +1,18 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+
+[project]
+name = "{{ app_name }}"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.10"
+dependencies = [
+ "reachy-mini"
+]
+keywords = ["reachy-mini-app"]
+
+[project.entry-points."reachy_mini_apps"]
+{{ app_name }} = "{{ module_name }}.main:{{ class_name }}"
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/apps/templates/style.css.j2 b/reachy_mini/src/reachy_mini/apps/templates/style.css.j2
new file mode 100644
index 0000000000000000000000000000000000000000..64fab914657a8fb5757e9038c31683d933f773d7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/templates/style.css.j2
@@ -0,0 +1,411 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ line-height: 1.6;
+ color: #333;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+}
+
+.hero {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ padding: 4rem 2rem;
+ text-align: center;
+}
+
+.hero-content {
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.app-icon {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+ display: inline-block;
+}
+
+.hero h1 {
+ font-size: 3rem;
+ font-weight: 700;
+ margin-bottom: 1rem;
+ background: linear-gradient(45deg, #fff, #f0f9ff);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.tagline {
+ font-size: 1.25rem;
+ opacity: 0.9;
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+ position: relative;
+ z-index: 2;
+}
+
+.main-card {
+ background: white;
+ border-radius: 20px;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ margin-top: -2rem;
+ overflow: hidden;
+ margin-bottom: 3rem;
+}
+
+.app-preview {
+ background: linear-gradient(135deg, #1e3a8a, #3b82f6);
+ padding: 3rem;
+ color: white;
+ text-align: center;
+ position: relative;
+}
+
+.preview-image {
+ background: #000;
+ border-radius: 15px;
+ padding: 2rem;
+ max-width: 500px;
+ margin: 0 auto;
+ position: relative;
+ overflow: hidden;
+}
+
+.camera-feed {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+ opacity: 0.7;
+}
+
+.detection-overlay {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 100%;
+}
+
+.bbox {
+ background: rgba(34, 197, 94, 0.9);
+ color: white;
+ padding: 0.5rem 1rem;
+ border-radius: 8px;
+ font-size: 0.9rem;
+ font-weight: 600;
+ margin: 0.5rem;
+ display: inline-block;
+ border: 2px solid #22c55e;
+}
+
+.app-details {
+ padding: 3rem;
+}
+
+.app-details h2 {
+ font-size: 2rem;
+ color: #1e293b;
+ margin-bottom: 2rem;
+ text-align: center;
+}
+
+.template-info {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ margin-bottom: 3rem;
+}
+
+.info-box {
+ background: #f0f9ff;
+ border: 2px solid #e0f2fe;
+ border-radius: 12px;
+ padding: 2rem;
+}
+
+.info-box h3 {
+ color: #0c4a6e;
+ margin-bottom: 1rem;
+ font-size: 1.2rem;
+}
+
+.info-box p {
+ color: #0369a1;
+ line-height: 1.6;
+}
+
+.how-to-use {
+ background: #fefce8;
+ border: 2px solid #fde047;
+ border-radius: 12px;
+ padding: 2rem;
+ margin-top: 3rem;
+}
+
+.how-to-use h3 {
+ color: #a16207;
+ margin-bottom: 1.5rem;
+ font-size: 1.3rem;
+ text-align: center;
+}
+
+.steps {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.step {
+ display: flex;
+ align-items: flex-start;
+ gap: 1rem;
+}
+
+.step-number {
+ background: #eab308;
+ color: white;
+ width: 2rem;
+ height: 2rem;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ flex-shrink: 0;
+}
+
+.step h4 {
+ color: #a16207;
+ margin-bottom: 0.5rem;
+ font-size: 1.1rem;
+}
+
+.step p {
+ color: #ca8a04;
+}
+
+.download-card {
+ background: white;
+ border-radius: 20px;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ padding: 3rem;
+ text-align: center;
+}
+
+.download-card h2 {
+ font-size: 2rem;
+ color: #1e293b;
+ margin-bottom: 1rem;
+}
+
+.download-card>p {
+ color: #64748b;
+ font-size: 1.1rem;
+ margin-bottom: 2rem;
+}
+
+.dashboard-config {
+ margin-bottom: 2rem;
+ text-align: left;
+ max-width: 400px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.dashboard-config label {
+ display: block;
+ color: #374151;
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+}
+
+.dashboard-config input {
+ width: 100%;
+ padding: 0.75rem 1rem;
+ border: 2px solid #e5e7eb;
+ border-radius: 8px;
+ font-size: 0.95rem;
+ transition: border-color 0.2s;
+}
+
+.dashboard-config input:focus {
+ outline: none;
+ border-color: #667eea;
+}
+
+.install-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ padding: 1.25rem 3rem;
+ border-radius: 16px;
+ font-size: 1.2rem;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 2rem;
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
+}
+
+.install-btn:hover:not(:disabled) {
+ transform: translateY(-3px);
+ box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4);
+}
+
+.install-btn:disabled {
+ opacity: 0.7;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.manual-option {
+ background: #f8fafc;
+ border-radius: 12px;
+ padding: 2rem;
+ margin-top: 2rem;
+}
+
+.manual-option h3 {
+ color: #1e293b;
+ margin-bottom: 1rem;
+ font-size: 1.2rem;
+}
+
+.manual-option>p {
+ color: #64748b;
+ margin-bottom: 1rem;
+}
+
+.btn-icon {
+ font-size: 1.1rem;
+}
+
+.install-status {
+ padding: 1rem;
+ border-radius: 8px;
+ font-size: 0.9rem;
+ text-align: center;
+ display: none;
+ margin-top: 1rem;
+}
+
+.install-status.success {
+ background: #dcfce7;
+ color: #166534;
+ border: 1px solid #bbf7d0;
+}
+
+.install-status.error {
+ background: #fef2f2;
+ color: #dc2626;
+ border: 1px solid #fecaca;
+}
+
+.install-status.loading {
+ background: #dbeafe;
+ color: #1d4ed8;
+ border: 1px solid #bfdbfe;
+}
+
+.install-status.info {
+ background: #e0f2fe;
+ color: #0369a1;
+ border: 1px solid #7dd3fc;
+}
+
+.manual-install {
+ background: #1f2937;
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.manual-install code {
+ color: #10b981;
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
+ font-size: 0.85rem;
+ flex: 1;
+ overflow-x: auto;
+}
+
+.copy-btn {
+ background: #374151;
+ color: white;
+ border: none;
+ padding: 0.5rem 1rem;
+ border-radius: 6px;
+ font-size: 0.8rem;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.copy-btn:hover {
+ background: #4b5563;
+}
+
+.manual-steps {
+ color: #6b7280;
+ font-size: 0.9rem;
+ line-height: 1.8;
+}
+
+.footer {
+ text-align: center;
+ padding: 2rem;
+ color: white;
+ opacity: 0.8;
+}
+
+.footer a {
+ color: white;
+ text-decoration: none;
+ font-weight: 600;
+}
+
+.footer a:hover {
+ text-decoration: underline;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .hero {
+ padding: 2rem 1rem;
+ }
+
+ .hero h1 {
+ font-size: 2rem;
+ }
+
+ .container {
+ padding: 0 1rem;
+ }
+
+ .app-details,
+ .download-card {
+ padding: 2rem;
+ }
+
+ .features-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .download-options {
+ grid-template-columns: 1fr;
+ }
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/apps/utils.py b/reachy_mini/src/reachy_mini/apps/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..f039ff996ba8924501483617ba931836f7142fdc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/apps/utils.py
@@ -0,0 +1,30 @@
+"""Utility functions for Reachy Mini apps manager."""
+
+import asyncio
+import logging
+
+
+async def running_command(command: list[str], logger: logging.Logger) -> int:
+ """Run a shell command and stream its output to the provided logger."""
+ logger.info(f"Running command: {' '.join(command)}")
+
+ proc = await asyncio.create_subprocess_exec(
+ *command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
+ )
+
+ assert proc.stdout is not None # for mypy
+ assert proc.stderr is not None # for mypy
+
+ # Stream output line by line
+ while True:
+ line = await proc.stdout.readline()
+ if not line:
+ break
+ logger.info(line.decode().rstrip())
+
+ # Also log any remaining stderr
+ err = await proc.stderr.read()
+ if err:
+ logger.error(err.decode().rstrip())
+
+ return await proc.wait()
diff --git a/reachy_mini/src/reachy_mini/assets/config/hardware_config.yaml b/reachy_mini/src/reachy_mini/assets/config/hardware_config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..18feed39927a238ac3828ba38f3fe425d22cadd0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/config/hardware_config.yaml
@@ -0,0 +1,155 @@
+version: beta
+
+serial:
+ baudrate: 1000000
+
+# Limits measured on the robot (in degrees)
+# s1
+# lower : -48° : -546
+# upper : +80° : 910
+
+# s2
+# lower: +70° : 796
+# upper: -80° : -910
+
+# s3
+# lower: -48° : -546
+# upper: +80° : 910
+
+# s4
+# lower: +48° : 546
+# upper: -80° : -910
+
+# s5
+# lower: -70° : -796
+# upper: +80° : 910
+
+# s6
+# lower : +48° : 546
+# upper : -80° : -910
+
+# s1:
+# lower: 2048 - 546 : 1502
+# upper: 2048 + 910 : 2958
+
+# s2:
+# lower: 2048 - 910 : 1138
+# upper: 2048 + 796 : 2844
+
+# s3:
+# lower: 2048 - 546 : 1502
+# upper: 2048 + 910 : 2958
+
+# s4:
+# lower: 2048 - 910 : 1138
+# upper: 2048 + 546 : 2594
+
+# s5:
+# lower: 2048 - 796 : 1252
+# upper: 2048 + 910 : 2958
+
+# s6:
+# lower: 2048 - 910 : 1138
+# upper: 2048 + 546 : 2594
+
+
+motors:
+ - body_rotation:
+ id: 10
+ offset: 0
+ lower_limit: 0
+ upper_limit: 4095
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_1:
+ id: 11
+ offset: 1024
+ lower_limit: 1502
+ upper_limit: 2958
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_2:
+ id: 12
+ offset: -1024
+ lower_limit: 1138
+ upper_limit: 2844
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_3:
+ id: 13
+ offset: 1024
+ lower_limit: 1502
+ upper_limit: 2958
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_4:
+ id: 14
+ offset: -1024
+ lower_limit: 1138
+ upper_limit: 2594
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_5:
+ id: 15
+ offset: 1024
+ lower_limit: 1252
+ upper_limit: 2958
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - stewart_6:
+ id: 16
+ offset: -1024
+ lower_limit: 1138
+ upper_limit: 2594
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 0
+ - right_antenna:
+ id: 17
+ offset: 0
+ lower_limit: 0
+ upper_limit: 4095
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 400
+ - left_antenna:
+ id: 18
+ offset: 0
+ lower_limit: 0
+ upper_limit: 4095
+ return_delay_time: 0
+ shutdown_error: 52
+ pid:
+ - 400
+ - 0
+ - 400
diff --git a/reachy_mini/src/reachy_mini/assets/firmware/CHANGELOG.md b/reachy_mini/src/reachy_mini/assets/firmware/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..db473c4690bf31e683494c8e314080a0efc41f60
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/firmware/CHANGELOG.md
@@ -0,0 +1,15 @@
+# Changelog of Reachy Mini Audio Firmware
+
+
+## 2.1.3
+
+*For Beta units only*
+
+Fixes the initialization issue on Reachy Mini beta hardware. An additional 2-second delay is added during initialization to prevent the XMOS chip from starting before the other components.
+
+There is no need to apply this firmware to the Lite and Wireless versions, as the issue is fixed at the hardware level.
+
+## 2.1.2
+
+Improved parameters for acoustic echo cancellation (AEC).
+PP_DTSENSITIVE is set to 1 by default.
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin b/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6314af04676bac273aab5a795a36632813afe685
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:661429a93b155cdbfed1e6c214820fd42e3372a08c3930f14fcd4e6db5be6796
+size 933888
diff --git a/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin b/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin
new file mode 100644
index 0000000000000000000000000000000000000000..680a99700de092ac1d191848518ea6aa9fe2c846
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e35fa9a43a4e75890fd6299e62e85310d8081087e6a9702aaef0c81bd3af283
+size 933888
diff --git a/reachy_mini/src/reachy_mini/assets/firmware/update.sh b/reachy_mini/src/reachy_mini/assets/firmware/update.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e6f0f3243a81e1edb4667e09660a30f13ae51b01
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/firmware/update.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# Firmware update script for Reachy Mini
+# Usage: ./update.sh
+firmware="$1"
+if [ -z "$firmware" ]; then
+ echo "Usage: $0 "
+ exit 1
+fi
+dfu-util -R -e -a 1 -D "$firmware"
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/assets/kinematics_data.json b/reachy_mini/src/reachy_mini/assets/kinematics_data.json
new file mode 100644
index 0000000000000000000000000000000000000000..c20b364f6c386f17f8cc2c3901df873583fc1d31
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/kinematics_data.json
@@ -0,0 +1,253 @@
+{
+ "motor_arm_length": 0.04000000000000001,
+ "rod_length": 0.08499999999999995,
+ "head_z_offset": 0.177,
+ "motors": [
+ {
+ "name": "stewart_1",
+ "branch_frame": "closing_1_2",
+ "offset": 0,
+ "solution": 0,
+ "T_motor_world": [
+ [
+ 0.8660247915798898,
+ -0.5000010603626028,
+ -2.298079077119539e-06,
+ -0.009999848080267933
+ ],
+ [
+ 4.490195936008854e-06,
+ 3.1810770986818273e-06,
+ 0.999999999984859,
+ -0.07663346037245178
+ ],
+ [
+ -0.500001060347722,
+ -0.8660247915770963,
+ 4.999994360718464e-06,
+ 0.03666015757925319
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ 0.020648178337122566,
+ 0.021763723638894568,
+ 1.0345743467476964e-07
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ },
+ {
+ "name": "stewart_2",
+ "branch_frame": "closing_2_2",
+ "offset": 0,
+ "solution": 1,
+ "T_motor_world": [
+ [
+ -0.8660211183436269,
+ 0.5000074225224785,
+ 2.298069723064582e-06,
+ -0.01000055227585102
+ ],
+ [
+ -4.490219645842903e-06,
+ -3.181063409649239e-06,
+ -0.999999999984859,
+ 0.07663346037219607
+ ],
+ [
+ -0.5000074225075973,
+ -0.8660211183408337,
+ 5.00001124330122e-06,
+ 0.03666008712637943
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ 0.00852381571767217,
+ 0.028763668526131346,
+ 1.183437210727778e-07
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ },
+ {
+ "name": "stewart_3",
+ "branch_frame": "closing_3_2",
+ "offset": 0,
+ "solution": 0,
+ "T_motor_world": [
+ [
+ 6.326794896519466e-06,
+ 0.9999999999799852,
+ -7.0550646912150425e-12,
+ -0.009999884140839245
+ ],
+ [
+ -1.0196153102346142e-06,
+ 1.3505961633338446e-11,
+ 0.9999999999994795,
+ -0.07663346037438698
+ ],
+ [
+ 0.9999999999794655,
+ -6.326794896940104e-06,
+ 1.0196153098685706e-06,
+ 0.036660683387545835
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ -0.029172011376922807,
+ 0.0069999429399361995,
+ 4.0290270064691214e-08
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ },
+ {
+ "name": "stewart_4",
+ "branch_frame": "closing_4_2",
+ "offset": 0,
+ "solution": 1,
+ "T_motor_world": [
+ [
+ -3.673205069955933e-06,
+ -0.9999999999932537,
+ -6.767968877969483e-14,
+ -0.010000000000897517
+ ],
+ [
+ 1.0196153102837198e-06,
+ -3.6775764393585005e-12,
+ -0.9999999999994795,
+ 0.0766334603742898
+ ],
+ [
+ 0.9999999999927336,
+ -3.673205070385213e-06,
+ 1.0196153102903487e-06,
+ 0.03666065685180194
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ -0.029172040355214434,
+ -0.0069999960097160766,
+ -3.1608172912367394e-08
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ },
+ {
+ "name": "stewart_5",
+ "branch_frame": "closing_5_2",
+ "offset": 0,
+ "solution": 0,
+ "T_motor_world": [
+ [
+ -0.8660284647694133,
+ -0.4999946981757419,
+ 2.298079429767357e-06,
+ -0.010000231529504576
+ ],
+ [
+ 4.490172883391843e-06,
+ -3.1811099293773187e-06,
+ 0.9999999999848591,
+ -0.07663346037246624
+ ],
+ [
+ -0.4999946981608617,
+ 0.8660284647666201,
+ 4.999994384073154e-06,
+ 0.03666016059492482
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ 0.008523809101930114,
+ -0.028763713010385224,
+ -1.4344916837716326e-07
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ },
+ {
+ "name": "stewart_6",
+ "branch_frame": "passive_7_link_y",
+ "offset": 0,
+ "solution": 1,
+ "T_motor_world": [
+ [
+ 0.8660247915798897,
+ 0.5000010603626025,
+ -2.298069644866714e-06,
+ -0.009999527331574583
+ ],
+ [
+ -4.490196220318687e-06,
+ 3.1810964558725514e-06,
+ -0.9999999999848591,
+ 0.07663346037272492
+ ],
+ [
+ -0.500001060347722,
+ 0.8660247915770967,
+ 5.000011266610794e-06,
+ 0.036660231042625266
+ ],
+ [
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "branch_position": [
+ 0.020648186722822436,
+ -0.02176369606185343,
+ -8.957920105689965e-08
+ ],
+ "limits": [
+ -3.141592653589793,
+ 3.141592653589793
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/assets/models/fknetwork.dynamic.onnx b/reachy_mini/src/reachy_mini/assets/models/fknetwork.dynamic.onnx
new file mode 100644
index 0000000000000000000000000000000000000000..c7edae28297689eed85a3306345b8931cd6be1c4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/models/fknetwork.dynamic.onnx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:baeb97ce5f28c649151bebe8fc6a0a8e481b21e8e31346c9a665e792e41f9289
+size 75827
diff --git a/reachy_mini/src/reachy_mini/assets/models/fknetwork.onnx b/reachy_mini/src/reachy_mini/assets/models/fknetwork.onnx
new file mode 100644
index 0000000000000000000000000000000000000000..67f4c79c4f44fe2a3bf8c6684143218d96b2b3bd
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/models/fknetwork.onnx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf0d2350c67636349a5f3de056cb8823a68fedfd31c4ac6fcde2dd245dee5f57
+size 75209
diff --git a/reachy_mini/src/reachy_mini/assets/models/fknetwork_int8.onnx b/reachy_mini/src/reachy_mini/assets/models/fknetwork_int8.onnx
new file mode 100644
index 0000000000000000000000000000000000000000..df0fd772e892eea675ed2667adb6121388297051
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/models/fknetwork_int8.onnx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c693ac748a340d1398d6d2b8b906e76ffe9aec066b709412b4a618b34f57422
+size 42162
diff --git a/reachy_mini/src/reachy_mini/assets/models/iknetwork.onnx b/reachy_mini/src/reachy_mini/assets/models/iknetwork.onnx
new file mode 100644
index 0000000000000000000000000000000000000000..7b955057ecabc5779fa30e3a826de13a809a2930
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/assets/models/iknetwork.onnx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:538be065edaf18f75ae05f4c17fe36b5b098cd5c72d6d4d3cb6152c40de68d27
+size 75213
diff --git a/reachy_mini/src/reachy_mini/daemon/__init__.py b/reachy_mini/src/reachy_mini/daemon/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..945ec88eb820cd78614e4fde4c38f86bcb615ffc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/__init__.py
@@ -0,0 +1 @@
+"""Daemon for Reachy Mini."""
diff --git a/reachy_mini/src/reachy_mini/daemon/app/__init__.py b/reachy_mini/src/reachy_mini/daemon/app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d7c49bda267ff648789e31738bbd5f0895a8108
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/__init__.py
@@ -0,0 +1 @@
+"""FastAPI app initialization."""
diff --git a/reachy_mini/src/reachy_mini/daemon/app/bg_job_register.py b/reachy_mini/src/reachy_mini/daemon/app/bg_job_register.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec4ae598cb1ec98bcefe1e1446284287cb8920ed
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/bg_job_register.py
@@ -0,0 +1,142 @@
+"""Background jobs management for Reachy Mini Daemon."""
+
+import asyncio
+import logging
+import threading
+import uuid
+from dataclasses import dataclass
+from enum import Enum
+from typing import Any, Awaitable, Callable
+
+from fastapi import WebSocket, WebSocketDisconnect
+from pydantic import BaseModel
+
+
+class JobStatus(Enum):
+ """Enum for job status."""
+
+ PENDING = "pending"
+ IN_PROGRESS = "in_progress"
+ DONE = "done"
+ FAILED = "failed"
+
+
+class JobInfo(BaseModel):
+ """Pydantic model for install job status."""
+
+ command: str
+ status: JobStatus
+ logs: list[str]
+
+
+@dataclass
+class JobHandler:
+ """Handler for background jobs."""
+
+ uuid: str
+ info: JobInfo
+ new_log_evt: dict[str, asyncio.Event]
+
+
+register: dict[str, JobHandler] = {}
+
+
+def run_command(
+ command: str,
+ coro_func: Callable[..., Awaitable[None]],
+ *args: Any,
+) -> str:
+ """Start a background job, with a custom logger and return its job_id."""
+ job_uuid = str(uuid.uuid4())
+
+ jh = JobHandler(
+ uuid=job_uuid,
+ info=JobInfo(command=command, status=JobStatus.PENDING, logs=[]),
+ new_log_evt={},
+ )
+ register[job_uuid] = jh
+
+ start_evt = threading.Event()
+
+ async def wrapper() -> None:
+ jh.info.status = JobStatus.IN_PROGRESS
+
+ class JobLogger(logging.Handler):
+ def emit(self, record: logging.LogRecord) -> None:
+ jh.info.logs.append(self.format(record))
+ for ws in jh.new_log_evt.values():
+ ws.set()
+
+ logger = logging.getLogger(f"logs_job_{job_uuid}")
+ logger.setLevel(logging.INFO)
+ logger.handlers.clear()
+ logger.addHandler(JobLogger())
+
+ start_evt.set()
+
+ try:
+ await coro_func(*args, logger=logger)
+ jh.info.status = JobStatus.DONE
+ logger.info(f"Job '{command}' completed successfully")
+ except Exception as e:
+ jh.info.status = JobStatus.FAILED
+ logger.error(f"Job '{command}' failed with error: {e}")
+
+ t = threading.Thread(target=lambda: asyncio.run(wrapper()))
+ t.start()
+ # background_tasks.add_task(wrapper)
+ start_evt.wait()
+
+ return job_uuid
+
+
+def get_info(job_id: str) -> JobInfo:
+ """Get the info of a job by its ID."""
+ job = register.get(job_id)
+
+ if not job:
+ raise ValueError("Job ID not found")
+
+ return job.info
+
+
+async def ws_poll_info(websocket: WebSocket, job_uuid: str) -> None:
+ """WebSocket endpoint to stream job logs in real time."""
+ job = register.get(job_uuid)
+ if not job:
+ await websocket.send_json({"error": "Job ID not found"})
+ await websocket.close()
+ return
+
+ assert job is not None
+
+ ws_uuid = str(uuid.uuid4())
+ last_log_len = 0
+
+ try:
+ job.new_log_evt[ws_uuid] = asyncio.Event()
+
+ while True:
+ await job.new_log_evt[ws_uuid].wait()
+ job.new_log_evt[ws_uuid].clear()
+
+ new_logs = job.info.logs[last_log_len:]
+
+ if new_logs:
+ for log_entry in new_logs:
+ await websocket.send_text(log_entry)
+ last_log_len = len(job.info.logs)
+
+ await websocket.send_text(
+ JobInfo(
+ command=job.info.command,
+ status=job.info.status,
+ logs=new_logs,
+ ).model_dump_json()
+ )
+ if job.info.status in (JobStatus.DONE, JobStatus.FAILED):
+ break
+ except WebSocketDisconnect:
+ pass
+ finally:
+ job.new_log_evt.pop(ws_uuid, None)
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg
new file mode 100644
index 0000000000000000000000000000000000000000..98262b9c367727ebbf14d39128164308f607286e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ad9c44f9971b0d5129493f099f182d6ea42d790f9d9e25e79b31acecba5e414
+size 5677
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a9c0a5395a9e595294a6cf3805b50a59a3fb5bec
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c752c76e3786b07cc3c25b97dc09da49d7388fdf2cb044d3747aa38f56535ae4
+size 544172
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a03162a458b2ab45182aea1039a4ebfb6b22316b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8867ef0f9bd9edd64c2863e99d722fbf45f9f0bb232c4a9df2e63c8c2c2a3213
+size 46179
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e38bf61908b47769784228f4d0316caeceb13d1e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6c68f0fdae00154dc687e68684c945dd4a8ffc9e626add679150174974a818d
+size 46008
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6a83dd99b256b478b6d9c64bb41329aa015bb4c8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2803946419f768faa7b9bc5c63a513fc677069df7c463981ac64f31e89f40cec
+size 7648
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c9509e78855e191f6dcd6b6aa5bce2599f25cbe9
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c5ff91ca4030d428fd71f9e572bd5a97be6c0704e98fcaecd5d985ee15a7b053
+size 47411
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7c5bd3e899bb9a6c224a859a515ab0975ad055e5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc04ba0461049d0ddb26bc18dd24d61a56743f7ae0a6b8967cf3c03e660187e4
+size 2541
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7217dc20bb2652bd538eef9a85842cf708e8c7b8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2bba63d3b2c480f92d551e1fdb0d372672793803caacd3aaeb896967c1a64cb
+size 44803
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg
new file mode 100644
index 0000000000000000000000000000000000000000..52e8e9386662924a4b77a56f50b199393f757d79
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6adbf645766a903289ec673ff3b04df5731691c0ab0c6a52cbb9bcd9f47f621
+size 43776
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b5b16f34cdbe57fa0bedbebfaf29b45c1909d2d3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c3c13332ae04bbb408be3f10bd176d9a6487c4daf110af95bb17f4526a5df510
+size 43684
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg
new file mode 100644
index 0000000000000000000000000000000000000000..14a872bfe00e3f2512f2788512299882a33a37f7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d98d1352eb846fd1ae44453ca082e382b9fae43690159d4660edd6a1dd15c739
+size 3005
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a8f7f0f3d4d0277960c29284664a11420de0e512
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:80dda399747505fba019b9392a151a90c812731fe31d5a06bb17276c61ad19d2
+size 6163
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9d96eadf7b0903d7832e6ca7aa4a2af9f65df11b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e36a601426886c7ee278b4e97e35a70f6616ec3583f9c89062bc39a868b8240d
+size 2520
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3872e016d9ed12c0442383b850f4097d88599105
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fb5546e131073597f6b1d0f06aabfca05639432b3af8d7667904ed827fc15809
+size 44077
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/css/app.css b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/css/app.css
new file mode 100644
index 0000000000000000000000000000000000000000..8d5a9c7ab463e68d51a3e19203c6af858b8b6e57
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/css/app.css
@@ -0,0 +1,1433 @@
+/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */
+@layer properties;
+@layer theme, base, components, utilities;
+@layer theme {
+ :root, :host {
+ --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
+ "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
+ "Courier New", monospace;
+ --color-red-300: oklch(80.8% 0.114 19.571);
+ --color-red-400: oklch(70.4% 0.191 22.216);
+ --color-red-500: oklch(63.7% 0.237 25.331);
+ --color-red-600: oklch(57.7% 0.245 27.325);
+ --color-red-700: oklch(50.5% 0.213 27.518);
+ --color-red-800: oklch(44.4% 0.177 26.899);
+ --color-yellow-500: oklch(79.5% 0.184 86.047);
+ --color-green-300: oklch(87.1% 0.15 154.449);
+ --color-green-400: oklch(79.2% 0.209 151.711);
+ --color-green-500: oklch(72.3% 0.219 149.579);
+ --color-green-600: oklch(62.7% 0.194 149.214);
+ --color-green-700: oklch(52.7% 0.154 150.069);
+ --color-green-800: oklch(44.8% 0.119 151.328);
+ --color-blue-300: oklch(80.9% 0.105 251.813);
+ --color-blue-500: oklch(62.3% 0.214 259.815);
+ --color-blue-600: oklch(54.6% 0.245 262.881);
+ --color-blue-700: oklch(48.8% 0.243 264.376);
+ --color-blue-800: oklch(42.4% 0.199 265.638);
+ --color-gray-50: oklch(98.5% 0.002 247.839);
+ --color-gray-100: oklch(96.7% 0.003 264.542);
+ --color-gray-200: oklch(92.8% 0.006 264.531);
+ --color-gray-300: oklch(87.2% 0.01 258.338);
+ --color-gray-400: oklch(70.7% 0.022 261.325);
+ --color-gray-500: oklch(55.1% 0.027 264.364);
+ --color-gray-600: oklch(44.6% 0.03 256.802);
+ --color-gray-700: oklch(37.3% 0.034 259.733);
+ --color-gray-900: oklch(21% 0.034 264.665);
+ --color-white: #fff;
+ --spacing: 0.25rem;
+ --container-md: 28rem;
+ --container-2xl: 42rem;
+ --text-xs: 0.75rem;
+ --text-xs--line-height: calc(1 / 0.75);
+ --text-sm: 0.875rem;
+ --text-sm--line-height: calc(1.25 / 0.875);
+ --text-base: 1rem;
+ --text-base--line-height: calc(1.5 / 1);
+ --text-lg: 1.125rem;
+ --text-lg--line-height: calc(1.75 / 1.125);
+ --text-xl: 1.25rem;
+ --text-xl--line-height: calc(1.75 / 1.25);
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+ --radius-lg: 0.5rem;
+ --default-transition-duration: 150ms;
+ --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ --default-font-family: var(--font-sans);
+ --default-mono-font-family: var(--font-mono);
+ }
+}
+@layer base {
+ *, ::after, ::before, ::backdrop, ::file-selector-button {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ border: 0 solid;
+ }
+ html, :host {
+ line-height: 1.5;
+ -webkit-text-size-adjust: 100%;
+ tab-size: 4;
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
+ font-feature-settings: var(--default-font-feature-settings, normal);
+ font-variation-settings: var(--default-font-variation-settings, normal);
+ -webkit-tap-highlight-color: transparent;
+ }
+ hr {
+ height: 0;
+ color: inherit;
+ border-top-width: 1px;
+ }
+ abbr:where([title]) {
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+ }
+ h1, h2, h3, h4, h5, h6 {
+ font-size: inherit;
+ font-weight: inherit;
+ }
+ a {
+ color: inherit;
+ -webkit-text-decoration: inherit;
+ text-decoration: inherit;
+ }
+ b, strong {
+ font-weight: bolder;
+ }
+ code, kbd, samp, pre {
+ font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
+ font-feature-settings: var(--default-mono-font-feature-settings, normal);
+ font-variation-settings: var(--default-mono-font-variation-settings, normal);
+ font-size: 1em;
+ }
+ small {
+ font-size: 80%;
+ }
+ sub, sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+ }
+ sub {
+ bottom: -0.25em;
+ }
+ sup {
+ top: -0.5em;
+ }
+ table {
+ text-indent: 0;
+ border-color: inherit;
+ border-collapse: collapse;
+ }
+ :-moz-focusring {
+ outline: auto;
+ }
+ progress {
+ vertical-align: baseline;
+ }
+ summary {
+ display: list-item;
+ }
+ ol, ul, menu {
+ list-style: none;
+ }
+ img, svg, video, canvas, audio, iframe, embed, object {
+ display: block;
+ vertical-align: middle;
+ }
+ img, video {
+ max-width: 100%;
+ height: auto;
+ }
+ button, input, select, optgroup, textarea, ::file-selector-button {
+ font: inherit;
+ font-feature-settings: inherit;
+ font-variation-settings: inherit;
+ letter-spacing: inherit;
+ color: inherit;
+ border-radius: 0;
+ background-color: transparent;
+ opacity: 1;
+ }
+ :where(select:is([multiple], [size])) optgroup {
+ font-weight: bolder;
+ }
+ :where(select:is([multiple], [size])) optgroup option {
+ padding-inline-start: 20px;
+ }
+ ::file-selector-button {
+ margin-inline-end: 4px;
+ }
+ ::placeholder {
+ opacity: 1;
+ }
+ @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
+ ::placeholder {
+ color: currentcolor;
+ @supports (color: color-mix(in lab, red, red)) {
+ color: color-mix(in oklab, currentcolor 50%, transparent);
+ }
+ }
+ }
+ textarea {
+ resize: vertical;
+ }
+ ::-webkit-search-decoration {
+ -webkit-appearance: none;
+ }
+ ::-webkit-date-and-time-value {
+ min-height: 1lh;
+ text-align: inherit;
+ }
+ ::-webkit-datetime-edit {
+ display: inline-flex;
+ }
+ ::-webkit-datetime-edit-fields-wrapper {
+ padding: 0;
+ }
+ ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
+ padding-block: 0;
+ }
+ ::-webkit-calendar-picker-indicator {
+ line-height: 1;
+ }
+ :-moz-ui-invalid {
+ box-shadow: none;
+ }
+ button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
+ appearance: button;
+ }
+ ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
+ height: auto;
+ }
+ [hidden]:where(:not([hidden="until-found"])) {
+ display: none !important;
+ }
+}
+@layer utilities {
+ .pointer-events-none {
+ pointer-events: none;
+ }
+ .sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip-path: inset(50%);
+ white-space: nowrap;
+ border-width: 0;
+ }
+ .absolute {
+ position: absolute;
+ }
+ .fixed {
+ position: fixed;
+ }
+ .relative {
+ position: relative;
+ }
+ .static {
+ position: static;
+ }
+ .inset-0 {
+ inset: calc(var(--spacing) * 0);
+ }
+ .start-1\.5 {
+ inset-inline-start: calc(var(--spacing) * 1.5);
+ }
+ .end-2\.5 {
+ inset-inline-end: calc(var(--spacing) * 2.5);
+ }
+ .top-0 {
+ top: calc(var(--spacing) * 0);
+ }
+ .top-0\.5 {
+ top: calc(var(--spacing) * 0.5);
+ }
+ .top-1\/2 {
+ top: calc(1/2 * 100%);
+ }
+ .right-0 {
+ right: calc(var(--spacing) * 0);
+ }
+ .left-0 {
+ left: calc(var(--spacing) * 0);
+ }
+ .left-0\.5 {
+ left: calc(var(--spacing) * 0.5);
+ }
+ .z-50 {
+ z-index: 50;
+ }
+ .row-span-2 {
+ grid-row: span 2 / span 2;
+ }
+ .container {
+ width: 100%;
+ @media (width >= 40rem) {
+ max-width: 40rem;
+ }
+ @media (width >= 48rem) {
+ max-width: 48rem;
+ }
+ @media (width >= 64rem) {
+ max-width: 64rem;
+ }
+ @media (width >= 80rem) {
+ max-width: 80rem;
+ }
+ @media (width >= 96rem) {
+ max-width: 96rem;
+ }
+ }
+ .mx-auto {
+ margin-inline: auto;
+ }
+ .my-1 {
+ margin-block: calc(var(--spacing) * 1);
+ }
+ .my-2 {
+ margin-block: calc(var(--spacing) * 2);
+ }
+ .mt-2 {
+ margin-top: calc(var(--spacing) * 2);
+ }
+ .mt-3 {
+ margin-top: calc(var(--spacing) * 3);
+ }
+ .mt-4 {
+ margin-top: calc(var(--spacing) * 4);
+ }
+ .mr-2 {
+ margin-right: calc(var(--spacing) * 2);
+ }
+ .mb-1 {
+ margin-bottom: calc(var(--spacing) * 1);
+ }
+ .mb-2 {
+ margin-bottom: calc(var(--spacing) * 2);
+ }
+ .mb-4 {
+ margin-bottom: calc(var(--spacing) * 4);
+ }
+ .ml-2 {
+ margin-left: calc(var(--spacing) * 2);
+ }
+ .block {
+ display: block;
+ }
+ .flex {
+ display: flex;
+ }
+ .grid {
+ display: grid;
+ }
+ .hidden {
+ display: none;
+ }
+ .inline {
+ display: inline;
+ }
+ .inline-block {
+ display: inline-block;
+ }
+ .size-7 {
+ width: calc(var(--spacing) * 7);
+ height: calc(var(--spacing) * 7);
+ }
+ .h-2 {
+ height: calc(var(--spacing) * 2);
+ }
+ .h-4 {
+ height: calc(var(--spacing) * 4);
+ }
+ .h-5 {
+ height: calc(var(--spacing) * 5);
+ }
+ .h-7 {
+ height: calc(var(--spacing) * 7);
+ }
+ .h-8 {
+ height: calc(var(--spacing) * 8);
+ }
+ .h-auto {
+ height: auto;
+ }
+ .h-full {
+ height: 100%;
+ }
+ .max-h-48 {
+ max-height: calc(var(--spacing) * 48);
+ }
+ .max-h-96 {
+ max-height: calc(var(--spacing) * 96);
+ }
+ .max-h-full {
+ max-height: 100%;
+ }
+ .min-h-screen {
+ min-height: 100vh;
+ }
+ .w-4 {
+ width: calc(var(--spacing) * 4);
+ }
+ .w-5 {
+ width: calc(var(--spacing) * 5);
+ }
+ .w-7 {
+ width: calc(var(--spacing) * 7);
+ }
+ .w-12 {
+ width: calc(var(--spacing) * 12);
+ }
+ .w-16 {
+ width: calc(var(--spacing) * 16);
+ }
+ .w-20 {
+ width: calc(var(--spacing) * 20);
+ }
+ .w-28 {
+ width: calc(var(--spacing) * 28);
+ }
+ .w-full {
+ width: 100%;
+ }
+ .max-w-2xl {
+ max-width: var(--container-2xl);
+ }
+ .max-w-md {
+ max-width: var(--container-md);
+ }
+ .min-w-\[50\%\] {
+ min-width: 50%;
+ }
+ .flex-1 {
+ flex: 1;
+ }
+ .flex-grow {
+ flex-grow: 1;
+ }
+ .-translate-x-4 {
+ --tw-translate-x: calc(var(--spacing) * -4);
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ .-translate-x-8 {
+ --tw-translate-x: calc(var(--spacing) * -8);
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ .-translate-y-1 {
+ --tw-translate-y: calc(var(--spacing) * -1);
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ .-translate-y-1\/2 {
+ --tw-translate-y: calc(calc(1/2 * 100%) * -1);
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ .transform {
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
+ }
+ .cursor-not-allowed {
+ cursor: not-allowed;
+ }
+ .cursor-pointer {
+ cursor: pointer;
+ }
+ .list-inside {
+ list-style-position: inside;
+ }
+ .list-disc {
+ list-style-type: disc;
+ }
+ .appearance-none {
+ appearance: none;
+ }
+ .grid-cols-\[2rem_auto_8rem\] {
+ grid-template-columns: 2rem auto 8rem;
+ }
+ .grid-cols-\[auto_6rem_2rem\] {
+ grid-template-columns: auto 6rem 2rem;
+ }
+ .flex-col {
+ flex-direction: column;
+ }
+ .flex-row {
+ flex-direction: row;
+ }
+ .items-center {
+ align-items: center;
+ }
+ .justify-between {
+ justify-content: space-between;
+ }
+ .justify-center {
+ justify-content: center;
+ }
+ .justify-stretch {
+ justify-content: stretch;
+ }
+ .gap-2 {
+ gap: calc(var(--spacing) * 2);
+ }
+ .gap-4 {
+ gap: calc(var(--spacing) * 4);
+ }
+ .space-y-4 {
+ :where(& > :not(:last-child)) {
+ --tw-space-y-reverse: 0;
+ margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
+ margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
+ }
+ }
+ .gap-x-2 {
+ column-gap: calc(var(--spacing) * 2);
+ }
+ .gap-x-6 {
+ column-gap: calc(var(--spacing) * 6);
+ }
+ .overflow-y-auto {
+ overflow-y: auto;
+ }
+ .rounded {
+ border-radius: 0.25rem;
+ }
+ .rounded-full {
+ border-radius: calc(infinity * 1px);
+ }
+ .rounded-lg {
+ border-radius: var(--radius-lg);
+ }
+ .rounded-t {
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+ }
+ .rounded-b {
+ border-bottom-right-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+ }
+ .border {
+ border-style: var(--tw-border-style);
+ border-width: 1px;
+ }
+ .border-t {
+ border-top-style: var(--tw-border-style);
+ border-top-width: 1px;
+ }
+ .border-b {
+ border-bottom-style: var(--tw-border-style);
+ border-bottom-width: 1px;
+ }
+ .border-l-2 {
+ border-left-style: var(--tw-border-style);
+ border-left-width: 2px;
+ }
+ .border-gray-200 {
+ border-color: var(--color-gray-200);
+ }
+ .border-gray-300 {
+ border-color: var(--color-gray-300);
+ }
+ .border-red-600 {
+ border-color: var(--color-red-600);
+ }
+ .bg-blue-500 {
+ background-color: var(--color-blue-500);
+ }
+ .bg-blue-600 {
+ background-color: var(--color-blue-600);
+ }
+ .bg-blue-700 {
+ background-color: var(--color-blue-700);
+ }
+ .bg-gray-50 {
+ background-color: var(--color-gray-50);
+ }
+ .bg-gray-100 {
+ background-color: var(--color-gray-100);
+ }
+ .bg-gray-200 {
+ background-color: var(--color-gray-200);
+ }
+ .bg-gray-300 {
+ background-color: var(--color-gray-300);
+ }
+ .bg-gray-400 {
+ background-color: var(--color-gray-400);
+ }
+ .bg-green-400 {
+ background-color: var(--color-green-400);
+ }
+ .bg-green-500 {
+ background-color: var(--color-green-500);
+ }
+ .bg-green-700 {
+ background-color: var(--color-green-700);
+ }
+ .bg-red-500 {
+ background-color: var(--color-red-500);
+ }
+ .bg-red-700 {
+ background-color: var(--color-red-700);
+ }
+ .bg-white {
+ background-color: var(--color-white);
+ }
+ .bg-yellow-500 {
+ background-color: var(--color-yellow-500);
+ }
+ .p-1 {
+ padding: calc(var(--spacing) * 1);
+ }
+ .p-2\.5 {
+ padding: calc(var(--spacing) * 2.5);
+ }
+ .p-4 {
+ padding: calc(var(--spacing) * 4);
+ }
+ .px-2 {
+ padding-inline: calc(var(--spacing) * 2);
+ }
+ .px-4 {
+ padding-inline: calc(var(--spacing) * 4);
+ }
+ .px-5 {
+ padding-inline: calc(var(--spacing) * 5);
+ }
+ .py-1 {
+ padding-block: calc(var(--spacing) * 1);
+ }
+ .py-2 {
+ padding-block: calc(var(--spacing) * 2);
+ }
+ .py-2\.5 {
+ padding-block: calc(var(--spacing) * 2.5);
+ }
+ .py-4 {
+ padding-block: calc(var(--spacing) * 4);
+ }
+ .pt-1 {
+ padding-top: calc(var(--spacing) * 1);
+ }
+ .pt-4 {
+ padding-top: calc(var(--spacing) * 4);
+ }
+ .pr-4 {
+ padding-right: calc(var(--spacing) * 4);
+ }
+ .pb-2 {
+ padding-bottom: calc(var(--spacing) * 2);
+ }
+ .pl-4 {
+ padding-left: calc(var(--spacing) * 4);
+ }
+ .text-center {
+ text-align: center;
+ }
+ .align-middle {
+ vertical-align: middle;
+ }
+ .font-mono {
+ font-family: var(--font-mono);
+ }
+ .text-base {
+ font-size: var(--text-base);
+ line-height: var(--tw-leading, var(--text-base--line-height));
+ }
+ .text-lg {
+ font-size: var(--text-lg);
+ line-height: var(--tw-leading, var(--text-lg--line-height));
+ }
+ .text-sm {
+ font-size: var(--text-sm);
+ line-height: var(--tw-leading, var(--text-sm--line-height));
+ }
+ .text-xl {
+ font-size: var(--text-xl);
+ line-height: var(--tw-leading, var(--text-xl--line-height));
+ }
+ .text-xs {
+ font-size: var(--text-xs);
+ line-height: var(--tw-leading, var(--text-xs--line-height));
+ }
+ .font-medium {
+ --tw-font-weight: var(--font-weight-medium);
+ font-weight: var(--font-weight-medium);
+ }
+ .font-semibold {
+ --tw-font-weight: var(--font-weight-semibold);
+ font-weight: var(--font-weight-semibold);
+ }
+ .text-blue-700 {
+ color: var(--color-blue-700);
+ }
+ .text-gray-400 {
+ color: var(--color-gray-400);
+ }
+ .text-gray-500 {
+ color: var(--color-gray-500);
+ }
+ .text-gray-600 {
+ color: var(--color-gray-600);
+ }
+ .text-gray-700 {
+ color: var(--color-gray-700);
+ }
+ .text-gray-900 {
+ color: var(--color-gray-900);
+ }
+ .text-green-700 {
+ color: var(--color-green-700);
+ }
+ .text-white {
+ color: var(--color-white);
+ }
+ .overline {
+ text-decoration-line: overline;
+ }
+ .opacity-50 {
+ opacity: 50%;
+ }
+ .shadow {
+ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ .shadow-sm {
+ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ .transition-all {
+ transition-property: all;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
+ .transition-colors {
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
+ .duration-150 {
+ --tw-duration: 150ms;
+ transition-duration: 150ms;
+ }
+ .duration-200 {
+ --tw-duration: 200ms;
+ transition-duration: 200ms;
+ }
+ .select-none {
+ -webkit-user-select: none;
+ user-select: none;
+ }
+ .peer-checked\:bg-blue-800 {
+ &:is(:where(.peer):checked ~ *) {
+ background-color: var(--color-blue-800);
+ }
+ }
+ .peer-checked\:bg-green-400 {
+ &:is(:where(.peer):checked ~ *) {
+ background-color: var(--color-green-400);
+ }
+ }
+ .peer-checked\:text-gray-400 {
+ &:is(:where(.peer):checked ~ *) {
+ color: var(--color-gray-400);
+ }
+ }
+ .peer-checked\:text-gray-900 {
+ &:is(:where(.peer):checked ~ *) {
+ color: var(--color-gray-900);
+ }
+ }
+ .peer-checked\:text-white {
+ &:is(:where(.peer):checked ~ *) {
+ color: var(--color-white);
+ }
+ }
+ .peer-focus\:ring-4 {
+ &:is(:where(.peer):focus ~ *) {
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ }
+ .peer-focus\:ring-gray-100 {
+ &:is(:where(.peer):focus ~ *) {
+ --tw-ring-color: var(--color-gray-100);
+ }
+ }
+ .peer-focus\:outline-none {
+ &:is(:where(.peer):focus ~ *) {
+ --tw-outline-style: none;
+ outline-style: none;
+ }
+ }
+ .after\:absolute {
+ &::after {
+ content: var(--tw-content);
+ position: absolute;
+ }
+ }
+ .after\:start-0\.5 {
+ &::after {
+ content: var(--tw-content);
+ inset-inline-start: calc(var(--spacing) * 0.5);
+ }
+ }
+ .after\:top-0\.5 {
+ &::after {
+ content: var(--tw-content);
+ top: calc(var(--spacing) * 0.5);
+ }
+ }
+ .after\:h-7 {
+ &::after {
+ content: var(--tw-content);
+ height: calc(var(--spacing) * 7);
+ }
+ }
+ .after\:w-7 {
+ &::after {
+ content: var(--tw-content);
+ width: calc(var(--spacing) * 7);
+ }
+ }
+ .after\:rounded-full {
+ &::after {
+ content: var(--tw-content);
+ border-radius: calc(infinity * 1px);
+ }
+ }
+ .after\:border {
+ &::after {
+ content: var(--tw-content);
+ border-style: var(--tw-border-style);
+ border-width: 1px;
+ }
+ }
+ .after\:border-gray-300 {
+ &::after {
+ content: var(--tw-content);
+ border-color: var(--color-gray-300);
+ }
+ }
+ .after\:bg-white {
+ &::after {
+ content: var(--tw-content);
+ background-color: var(--color-white);
+ }
+ }
+ .after\:transition-all {
+ &::after {
+ content: var(--tw-content);
+ transition-property: all;
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
+ }
+ }
+ .after\:content-\[\'\'\] {
+ &::after {
+ --tw-content: '';
+ content: var(--tw-content);
+ }
+ }
+ .peer-checked\:after\:translate-x-\[46px\] {
+ &:is(:where(.peer):checked ~ *) {
+ &::after {
+ content: var(--tw-content);
+ --tw-translate-x: 46px;
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ }
+ }
+ .peer-checked\:after\:border-white {
+ &:is(:where(.peer):checked ~ *) {
+ &::after {
+ content: var(--tw-content);
+ border-color: var(--color-white);
+ }
+ }
+ }
+ .hover\:bg-blue-600 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-blue-600);
+ }
+ }
+ }
+ .hover\:bg-blue-700 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-blue-700);
+ }
+ }
+ }
+ .hover\:bg-blue-800 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-blue-800);
+ }
+ }
+ }
+ .hover\:bg-gray-200 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-gray-200);
+ }
+ }
+ }
+ .hover\:bg-gray-300 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-gray-300);
+ }
+ }
+ }
+ .hover\:bg-green-500 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-green-500);
+ }
+ }
+ }
+ .hover\:bg-green-800 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-green-800);
+ }
+ }
+ }
+ .hover\:bg-red-600 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-red-600);
+ }
+ }
+ }
+ .hover\:bg-red-800 {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-red-800);
+ }
+ }
+ }
+ .hover\:text-gray-900 {
+ &:hover {
+ @media (hover: hover) {
+ color: var(--color-gray-900);
+ }
+ }
+ }
+ .focus\:border-blue-500 {
+ &:focus {
+ border-color: var(--color-blue-500);
+ }
+ }
+ .focus\:ring-4 {
+ &:focus {
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+ }
+ }
+ .focus\:ring-blue-300 {
+ &:focus {
+ --tw-ring-color: var(--color-blue-300);
+ }
+ }
+ .focus\:ring-blue-500 {
+ &:focus {
+ --tw-ring-color: var(--color-blue-500);
+ }
+ }
+ .focus\:ring-green-300 {
+ &:focus {
+ --tw-ring-color: var(--color-green-300);
+ }
+ }
+ .focus\:ring-red-300 {
+ &:focus {
+ --tw-ring-color: var(--color-red-300);
+ }
+ }
+ .focus\:outline-none {
+ &:focus {
+ --tw-outline-style: none;
+ outline-style: none;
+ }
+ }
+ .disabled\:bg-gray-300 {
+ &:disabled {
+ background-color: var(--color-gray-300);
+ }
+ }
+ .disabled\:text-gray-500 {
+ &:disabled {
+ color: var(--color-gray-500);
+ }
+ }
+ .sm\:w-auto {
+ @media (width >= 40rem) {
+ width: auto;
+ }
+ }
+ .sm\:px-8 {
+ @media (width >= 40rem) {
+ padding-inline: calc(var(--spacing) * 8);
+ }
+ }
+ .md\:w-2\/3 {
+ @media (width >= 48rem) {
+ width: calc(2/3 * 100%);
+ }
+ }
+ .md\:flex-row {
+ @media (width >= 48rem) {
+ flex-direction: row;
+ }
+ }
+ .md\:p-5 {
+ @media (width >= 48rem) {
+ padding: calc(var(--spacing) * 5);
+ }
+ }
+ .md\:px-8 {
+ @media (width >= 48rem) {
+ padding-inline: calc(var(--spacing) * 8);
+ }
+ }
+ .lg\:px-8 {
+ @media (width >= 64rem) {
+ padding-inline: calc(var(--spacing) * 8);
+ }
+ }
+ .xl\:px-8 {
+ @media (width >= 80rem) {
+ padding-inline: calc(var(--spacing) * 8);
+ }
+ }
+ .rtl\:peer-checked\:after\:-translate-x-full {
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
+ &:is(:where(.peer):checked ~ *) {
+ &::after {
+ content: var(--tw-content);
+ --tw-translate-x: -100%;
+ translate: var(--tw-translate-x) var(--tw-translate-y);
+ }
+ }
+ }
+ }
+ .dark\:border-gray-600 {
+ @media (prefers-color-scheme: dark) {
+ border-color: var(--color-gray-600);
+ }
+ }
+ .dark\:bg-blue-600 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-blue-600);
+ }
+ }
+ .dark\:bg-gray-400 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-gray-400);
+ }
+ }
+ .dark\:bg-gray-700 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-gray-700);
+ }
+ }
+ .dark\:bg-green-600 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-green-600);
+ }
+ }
+ .dark\:bg-red-400 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-red-400);
+ }
+ }
+ .dark\:bg-red-600 {
+ @media (prefers-color-scheme: dark) {
+ background-color: var(--color-red-600);
+ }
+ }
+ .dark\:text-gray-400 {
+ @media (prefers-color-scheme: dark) {
+ color: var(--color-gray-400);
+ }
+ }
+ .dark\:text-white {
+ @media (prefers-color-scheme: dark) {
+ color: var(--color-white);
+ }
+ }
+ .dark\:peer-checked\:bg-blue-800 {
+ @media (prefers-color-scheme: dark) {
+ &:is(:where(.peer):checked ~ *) {
+ background-color: var(--color-blue-800);
+ }
+ }
+ }
+ .dark\:peer-checked\:bg-green-400 {
+ @media (prefers-color-scheme: dark) {
+ &:is(:where(.peer):checked ~ *) {
+ background-color: var(--color-green-400);
+ }
+ }
+ }
+ .dark\:peer-focus\:ring-gray-100 {
+ @media (prefers-color-scheme: dark) {
+ &:is(:where(.peer):focus ~ *) {
+ --tw-ring-color: var(--color-gray-100);
+ }
+ }
+ }
+ .dark\:hover\:bg-blue-700 {
+ @media (prefers-color-scheme: dark) {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-blue-700);
+ }
+ }
+ }
+ }
+ .dark\:hover\:bg-green-700 {
+ @media (prefers-color-scheme: dark) {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-green-700);
+ }
+ }
+ }
+ }
+ .dark\:hover\:bg-red-700 {
+ @media (prefers-color-scheme: dark) {
+ &:hover {
+ @media (hover: hover) {
+ background-color: var(--color-red-700);
+ }
+ }
+ }
+ }
+ .dark\:focus\:ring-blue-800 {
+ @media (prefers-color-scheme: dark) {
+ &:focus {
+ --tw-ring-color: var(--color-blue-800);
+ }
+ }
+ }
+ .dark\:focus\:ring-green-800 {
+ @media (prefers-color-scheme: dark) {
+ &:focus {
+ --tw-ring-color: var(--color-green-800);
+ }
+ }
+ }
+ .dark\:focus\:ring-red-800 {
+ @media (prefers-color-scheme: dark) {
+ &:focus {
+ --tw-ring-color: var(--color-red-800);
+ }
+ }
+ }
+}
+@font-face {
+ font-family: 'Asap';
+ src: url('/static/fonts/Asap-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'Asap';
+ src: url('/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: italic;
+}
+@font-face {
+ font-family: 'Archivo';
+ src: url('/static/fonts/Archivo-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'Archivo';
+ src: url('/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: italic;
+}
+html {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+body {
+ font-family: 'Archivo', 'Asap', sans-serif;
+}
+.daemon-status-title {
+ font-size: 36px;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+}
+.daemon-status-collapse {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 0.7);
+}
+.app-section {
+ border-radius: 8px;
+ border-width: 1px;
+ padding: 16px;
+ gap: 16px;
+ background: #FFFFFF;
+}
+.installed-app-title {
+ font-weight: 600;
+}
+.app-section-title {
+ font-weight: 500;
+ font-size: 24px;
+ letter-spacing: 0px;
+}
+.app-list-item {
+ padding: 4px;
+}
+.hf-app-icon {
+ width: 42px;
+ height: 42px;
+ border-radius: 10px;
+ text-align: center;
+ font-size: 31px;
+ background-color: rgba(0, 0, 0, 0.1);
+}
+.hf-app-title {
+ font-weight: 500;
+ font-size: 18px;
+}
+.hf-app-description {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 75%;
+ color: rgba(0, 0, 0, 0.4);
+}
+.hf-app-install-button {
+ font-weight: 500;
+ font-size: 20px;
+ font-style: Bold;
+ text-align: center;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.wifi-status {
+ font-family: 'Asap', sans-serif;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.9);
+}
+.wifi-status-info {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 0.7);
+}
+.notification-modal {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ z-index: 9999;
+ min-width: 300px;
+ max-width: 400px;
+ background: #fff;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
+ border-radius: 8px;
+ display: none;
+ animation: fadeInUp 0.3s;
+}
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(40px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+.notification-content {
+ padding: 20px 28px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+.notification-content.info {
+ background: #fff;
+ border: 1px solid #e0e0e0;
+}
+.notification-content.error {
+ background: #ffeaea;
+ border: 1px solid #ff4d4f;
+ color: #b71c1c;
+}
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 1.5em;
+ cursor: pointer;
+ color: #888;
+}
+@property --tw-translate-x {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+@property --tw-translate-y {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+@property --tw-translate-z {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+@property --tw-rotate-x {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-rotate-y {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-rotate-z {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-skew-x {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-skew-y {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-space-y-reverse {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0;
+}
+@property --tw-border-style {
+ syntax: "*";
+ inherits: false;
+ initial-value: solid;
+}
+@property --tw-font-weight {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+@property --tw-shadow-color {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-shadow-alpha {
+ syntax: "";
+ inherits: false;
+ initial-value: 100%;
+}
+@property --tw-inset-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+@property --tw-inset-shadow-color {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-inset-shadow-alpha {
+ syntax: "";
+ inherits: false;
+ initial-value: 100%;
+}
+@property --tw-ring-color {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-ring-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+@property --tw-inset-ring-color {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-inset-ring-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+@property --tw-ring-inset {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-ring-offset-width {
+ syntax: "";
+ inherits: false;
+ initial-value: 0px;
+}
+@property --tw-ring-offset-color {
+ syntax: "*";
+ inherits: false;
+ initial-value: #fff;
+}
+@property --tw-ring-offset-shadow {
+ syntax: "*";
+ inherits: false;
+ initial-value: 0 0 #0000;
+}
+@property --tw-duration {
+ syntax: "*";
+ inherits: false;
+}
+@property --tw-content {
+ syntax: "*";
+ initial-value: "";
+ inherits: false;
+}
+@layer properties {
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
+ *, ::before, ::after, ::backdrop {
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-translate-z: 0;
+ --tw-rotate-x: initial;
+ --tw-rotate-y: initial;
+ --tw-rotate-z: initial;
+ --tw-skew-x: initial;
+ --tw-skew-y: initial;
+ --tw-space-y-reverse: 0;
+ --tw-border-style: solid;
+ --tw-font-weight: initial;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-color: initial;
+ --tw-shadow-alpha: 100%;
+ --tw-inset-shadow: 0 0 #0000;
+ --tw-inset-shadow-color: initial;
+ --tw-inset-shadow-alpha: 100%;
+ --tw-ring-color: initial;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-inset-ring-color: initial;
+ --tw-inset-ring-shadow: 0 0 #0000;
+ --tw-ring-inset: initial;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-duration: initial;
+ --tw-content: "";
+ }
+ }
+}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..a8e1dfe53db7cc7d533e524db69fe0ba7a3cccec
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js
@@ -0,0 +1,5 @@
+/*! gstwebrtc-api (https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/gstwebrtc-api), MPL-2.0 License, Copyright (C) 2022 Igalia S.L. , Author: Loïc Le Page */
+/*! Contains embedded adapter from webrtc-adapter (https://github.com/webrtcHacks/adapter), BSD 3-Clause License, Copyright (c) 2014, The WebRTC project authors. All rights reserved. Copyright (c) 2018, The adapter.js project authors. All rights reserved. */
+
+(()=>{"use strict";var e={539:e=>{const t={generateIdentifier:function(){return Math.random().toString(36).substring(2,12)}};t.localCName=t.generateIdentifier(),t.splitLines=function(e){return e.trim().split("\n").map((e=>e.trim()))},t.splitSections=function(e){return e.split("\nm=").map(((e,t)=>(t>0?"m="+e:e).trim()+"\r\n"))},t.getDescription=function(e){const n=t.splitSections(e);return n&&n[0]},t.getMediaSections=function(e){const n=t.splitSections(e);return n.shift(),n},t.matchPrefix=function(e,n){return t.splitLines(e).filter((e=>0===e.indexOf(n)))},t.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1],attributes:t.slice(2).join(" ")}},t.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+(e.attributes?" "+e.attributes:"")+"\r\n"},t.parseFmtp=function(e){const t={};let n;const r=e.substring(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?r.push(t+"="+e.parameters[t]):r.push(t)})),t+="a=fmtp:"+n+" "+r.join(";")+"\r\n"}return t},t.parseRtcpFb=function(e){const t=e.substring(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},t.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach((e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"})),t},t.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substring(7,t),10)},r=e.indexOf(":",t);return r>-1?(n.attribute=e.substring(t+1,r),n.value=e.substring(r+1)):n.attribute=e.substring(t+1),n},t.parseSsrcGroup=function(e){const t=e.substring(13).split(" ");return{semantics:t.shift(),ssrcs:t.map((e=>parseInt(e,10)))}},t.getMid=function(e){const n=t.matchPrefix(e,"a=mid:")[0];if(n)return n.substring(6)},t.parseFingerprint=function(e){const t=e.substring(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},t.getDtlsParameters=function(e,n){return{role:"auto",fingerprints:t.matchPrefix(e+n,"a=fingerprint:").map(t.parseFingerprint)}},t.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach((e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"})),n},t.parseCryptoLine=function(e){const t=e.substring(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},t.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?t.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},t.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substring(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},t.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},t.getCryptoParameters=function(e,n){return t.matchPrefix(e+n,"a=crypto:").map(t.parseCryptoLine)},t.getIceParameters=function(e,n){const r=t.matchPrefix(e+n,"a=ice-ufrag:")[0],i=t.matchPrefix(e+n,"a=ice-pwd:")[0];return r&&i?{usernameFragment:r.substring(12),password:i.substring(10)}:null},t.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},t.parseRtpParameters=function(e){const n={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=t.splitLines(e)[0].split(" ");n.profile=r[2];for(let i=3;i{n.headerExtensions.push(t.parseExtmap(e))}));const i=t.matchPrefix(e,"a=rtcp-fb:* ").map(t.parseRtcpFb);return n.codecs.forEach((e=>{i.forEach((t=>{e.rtcpFeedback.find((e=>e.type===t.type&&e.parameter===t.parameter))||e.rtcpFeedback.push(t)}))})),n},t.writeRtpDescription=function(e,n){let r="";r+="m="+e+" ",r+=n.codecs.length>0?"9":"0",r+=" "+(n.profile||"UDP/TLS/RTP/SAVPF")+" ",r+=n.codecs.map((e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType)).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",n.codecs.forEach((e=>{r+=t.writeRtpMap(e),r+=t.writeFmtp(e),r+=t.writeRtcpFb(e)}));let i=0;return n.codecs.forEach((e=>{e.maxptime>i&&(i=e.maxptime)})),i>0&&(r+="a=maxptime:"+i+"\r\n"),n.headerExtensions&&n.headerExtensions.forEach((e=>{r+=t.writeExtmap(e)})),r},t.parseRtpEncodingParameters=function(e){const n=[],r=t.parseRtpParameters(e),i=-1!==r.fecMechanisms.indexOf("RED"),o=-1!==r.fecMechanisms.indexOf("ULPFEC"),s=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"cname"===e.attribute)),a=s.length>0&&s[0].ssrc;let c;const d=t.matchPrefix(e,"a=ssrc-group:FID").map((e=>e.substring(17).split(" ").map((e=>parseInt(e,10)))));d.length>0&&d[0].length>1&&d[0][0]===a&&(c=d[0][1]),r.codecs.forEach((e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let t={ssrc:a,codecPayloadType:parseInt(e.parameters.apt,10)};a&&c&&(t.rtx={ssrc:c}),n.push(t),i&&(t=JSON.parse(JSON.stringify(t)),t.fec={ssrc:a,mechanism:o?"red+ulpfec":"red"},n.push(t))}})),0===n.length&&a&&n.push({ssrc:a});let l=t.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substring(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substring(5),10)*.95-16e3:void 0,n.forEach((e=>{e.maxBitrate=l}))),n},t.parseRtcpParameters=function(e){const n={},r=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"cname"===e.attribute))[0];r&&(n.cname=r.value,n.ssrc=r.ssrc);const i=t.matchPrefix(e,"a=rtcp-rsize");n.reducedSize=i.length>0,n.compound=0===i.length;const o=t.matchPrefix(e,"a=rtcp-mux");return n.mux=o.length>0,n},t.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},t.parseMsid=function(e){let n;const r=t.matchPrefix(e,"a=msid:");if(1===r.length)return n=r[0].substring(7).split(" "),{stream:n[0],track:n[1]};const i=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"msid"===e.attribute));return i.length>0?(n=i[0].value.split(" "),{stream:n[0],track:n[1]}):void 0},t.parseSctpDescription=function(e){const n=t.parseMLine(e),r=t.matchPrefix(e,"a=max-message-size:");let i;r.length>0&&(i=parseInt(r[0].substring(19),10)),isNaN(i)&&(i=65536);const o=t.matchPrefix(e,"a=sctp-port:");if(o.length>0)return{port:parseInt(o[0].substring(12),10),protocol:n.fmt,maxMessageSize:i};const s=t.matchPrefix(e,"a=sctpmap:");if(s.length>0){const e=s[0].substring(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:i}}},t.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},t.generateSessionId=function(){return Math.random().toString().substr(2,22)},t.writeSessionBoilerplate=function(e,n,r){let i;const o=void 0!==n?n:2;i=e||t.generateSessionId();return"v=0\r\no="+(r||"thisisadapterortc")+" "+i+" "+o+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},t.getDirection=function(e,n){const r=t.splitLines(e);for(let e=0;e{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e={};n.r(e),n.d(e,{fixNegotiationNeeded:()=>w,shimAddTrackRemoveTrack:()=>P,shimAddTrackRemoveTrackWithNative:()=>E,shimGetDisplayMedia:()=>y,shimGetSendersWithDtmf:()=>S,shimGetStats:()=>k,shimGetUserMedia:()=>C,shimMediaStream:()=>v,shimOnTrack:()=>b,shimPeerConnection:()=>R,shimSenderReceiverGetStats:()=>T});var t={};n.r(t),n.d(t,{shimAddTransceiver:()=>G,shimCreateAnswer:()=>U,shimCreateOffer:()=>N,shimGetDisplayMedia:()=>x,shimGetParameters:()=>j,shimGetUserMedia:()=>A,shimOnTrack:()=>I,shimPeerConnection:()=>O,shimRTCDataChannel:()=>H,shimReceiverGetStats:()=>M,shimRemoveStream:()=>L,shimSenderGetStats:()=>D});var r={};n.r(r),n.d(r,{shimAudioContext:()=>B,shimCallbacksAPI:()=>J,shimConstraints:()=>q,shimCreateOfferLegacy:()=>V,shimGetUserMedia:()=>K,shimLocalStreamsAPI:()=>F,shimRTCIceServerUrls:()=>W,shimRemoteStreamsAPI:()=>z,shimTrackEventTransceiver:()=>Y});var i={};n.r(i),n.d(i,{removeExtmapAllowMixed:()=>re,shimAddIceCandidateNullOrEmpty:()=>ie,shimConnectionState:()=>ne,shimMaxMessageSize:()=>ee,shimParameterlessSetLocalDescription:()=>oe,shimRTCIceCandidate:()=>$,shimRTCIceCandidateRelayProtocol:()=>Q,shimSendThrowTypeError:()=>te});let o=!0,s=!0;function a(e,t,n){const r=e.match(t);return r&&r.length>=n&&parseInt(r[n],10)}function c(e,t,n){if(!e.RTCPeerConnection)return;const r=e.RTCPeerConnection.prototype,i=r.addEventListener;r.addEventListener=function(e,r){if(e!==t)return i.apply(this,arguments);const o=e=>{const t=n(e);t&&(r.handleEvent?r.handleEvent(t):r(t))};return this._eventMap=this._eventMap||{},this._eventMap[t]||(this._eventMap[t]=new Map),this._eventMap[t].set(r,o),i.apply(this,[e,o])};const o=r.removeEventListener;r.removeEventListener=function(e,n){if(e!==t||!this._eventMap||!this._eventMap[t])return o.apply(this,arguments);if(!this._eventMap[t].has(n))return o.apply(this,arguments);const r=this._eventMap[t].get(n);return this._eventMap[t].delete(n),0===this._eventMap[t].size&&delete this._eventMap[t],0===Object.keys(this._eventMap).length&&delete this._eventMap,o.apply(this,[e,r])},Object.defineProperty(r,"on"+t,{get(){return this["_on"+t]},set(e){this["_on"+t]&&(this.removeEventListener(t,this["_on"+t]),delete this["_on"+t]),e&&this.addEventListener(t,this["_on"+t]=e)},enumerable:!0,configurable:!0})}function d(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(o=e,e?"adapter.js logging disabled":"adapter.js logging enabled")}function l(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(s=!e,"adapter.js deprecation warnings "+(e?"disabled":"enabled"))}function h(){if("object"==typeof window){if(o)return;"undefined"!=typeof console&&"function"==typeof console.log&&console.log.apply(console,arguments)}}function p(e,t){s&&console.warn(e+" is deprecated, please use "+t+" instead.")}function u(e){return"[object Object]"===Object.prototype.toString.call(e)}function m(e){return u(e)?Object.keys(e).reduce((function(t,n){const r=u(e[n]),i=r?m(e[n]):e[n],o=r&&!Object.keys(i).length;return void 0===i||o?t:Object.assign(t,{[n]:i})}),{}):e}function _(e,t,n){t&&!n.has(t.id)&&(n.set(t.id,t),Object.keys(t).forEach((r=>{r.endsWith("Id")?_(e,e.get(t[r]),n):r.endsWith("Ids")&&t[r].forEach((t=>{_(e,e.get(t),n)}))})))}function f(e,t,n){const r=n?"outbound-rtp":"inbound-rtp",i=new Map;if(null===t)return i;const o=[];return e.forEach((e=>{"track"===e.type&&e.trackIdentifier===t.id&&o.push(e)})),o.forEach((t=>{e.forEach((n=>{n.type===r&&n.trackId===t.id&&_(e,n,i)}))})),i}const g=h;function C(e,t){const n=e&&e.navigator;if(!n.mediaDevices)return;const r=function(e){if("object"!=typeof e||e.mandatory||e.optional)return e;const t={};return Object.keys(e).forEach((n=>{if("require"===n||"advanced"===n||"mediaSource"===n)return;const r="object"==typeof e[n]?e[n]:{ideal:e[n]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);const i=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==r.ideal){t.optional=t.optional||[];let e={};"number"==typeof r.ideal?(e[i("min",n)]=r.ideal,t.optional.push(e),e={},e[i("max",n)]=r.ideal,t.optional.push(e)):(e[i("",n)]=r.ideal,t.optional.push(e))}void 0!==r.exact&&"number"!=typeof r.exact?(t.mandatory=t.mandatory||{},t.mandatory[i("",n)]=r.exact):["min","max"].forEach((e=>{void 0!==r[e]&&(t.mandatory=t.mandatory||{},t.mandatory[i(e,n)]=r[e])}))})),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},i=function(e,i){if(t.version>=61)return i(e);if((e=JSON.parse(JSON.stringify(e)))&&"object"==typeof e.audio){const t=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])};t((e=JSON.parse(JSON.stringify(e))).audio,"autoGainControl","googAutoGainControl"),t(e.audio,"noiseSuppression","googNoiseSuppression"),e.audio=r(e.audio)}if(e&&"object"==typeof e.video){let o=e.video.facingMode;o=o&&("object"==typeof o?o:{ideal:o});const s=t.version<66;if(o&&("user"===o.exact||"environment"===o.exact||"user"===o.ideal||"environment"===o.ideal)&&(!n.mediaDevices.getSupportedConstraints||!n.mediaDevices.getSupportedConstraints().facingMode||s)){let t;if(delete e.video.facingMode,"environment"===o.exact||"environment"===o.ideal?t=["back","rear"]:"user"!==o.exact&&"user"!==o.ideal||(t=["front"]),t)return n.mediaDevices.enumerateDevices().then((n=>{let s=(n=n.filter((e=>"videoinput"===e.kind))).find((e=>t.some((t=>e.label.toLowerCase().includes(t)))));return!s&&n.length&&t.includes("back")&&(s=n[n.length-1]),s&&(e.video.deviceId=o.exact?{exact:s.deviceId}:{ideal:s.deviceId}),e.video=r(e.video),g("chrome: "+JSON.stringify(e)),i(e)}))}e.video=r(e.video)}return g("chrome: "+JSON.stringify(e)),i(e)},o=function(e){return t.version>=64?e:{name:{PermissionDeniedError:"NotAllowedError",PermissionDismissedError:"NotAllowedError",InvalidStateError:"NotAllowedError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotAllowedError",MediaDeviceKillSwitchOn:"NotAllowedError",TabCaptureError:"AbortError",ScreenCaptureError:"AbortError",DeviceCaptureError:"AbortError"}[e.name]||e.name,message:e.message,constraint:e.constraint||e.constraintName,toString(){return this.name+(this.message&&": ")+this.message}}};if(n.getUserMedia=function(e,t,r){i(e,(e=>{n.webkitGetUserMedia(e,t,(e=>{r&&r(o(e))}))}))}.bind(n),n.mediaDevices.getUserMedia){const e=n.mediaDevices.getUserMedia.bind(n.mediaDevices);n.mediaDevices.getUserMedia=function(t){return i(t,(t=>e(t).then((e=>{if(t.audio&&!e.getAudioTracks().length||t.video&&!e.getVideoTracks().length)throw e.getTracks().forEach((e=>{e.stop()})),new DOMException("","NotFoundError");return e}),(e=>Promise.reject(o(e))))))}}}function y(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&("function"==typeof t?e.navigator.mediaDevices.getDisplayMedia=function(n){return t(n).then((t=>{const r=n.video&&n.video.width,i=n.video&&n.video.height,o=n.video&&n.video.frameRate;return n.video={mandatory:{chromeMediaSource:"desktop",chromeMediaSourceId:t,maxFrameRate:o||3}},r&&(n.video.mandatory.maxWidth=r),i&&(n.video.mandatory.maxHeight=i),e.navigator.mediaDevices.getUserMedia(n)}))}:console.error("shimGetDisplayMedia: getSourceId argument is not a function"))}function v(e){e.MediaStream=e.MediaStream||e.webkitMediaStream}function b(e){if("object"==typeof e&&e.RTCPeerConnection&&!("ontrack"in e.RTCPeerConnection.prototype)){Object.defineProperty(e.RTCPeerConnection.prototype,"ontrack",{get(){return this._ontrack},set(e){this._ontrack&&this.removeEventListener("track",this._ontrack),this.addEventListener("track",this._ontrack=e)},enumerable:!0,configurable:!0});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){return this._ontrackpoly||(this._ontrackpoly=t=>{t.stream.addEventListener("addtrack",(n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find((e=>e.track&&e.track.id===n.track.id)):{track:n.track};const i=new Event("track");i.track=n.track,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)})),t.stream.getTracks().forEach((n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find((e=>e.track&&e.track.id===n.id)):{track:n};const i=new Event("track");i.track=n,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)}))},this.addEventListener("addstream",this._ontrackpoly)),t.apply(this,arguments)}}else c(e,"track",(e=>(e.transceiver||Object.defineProperty(e,"transceiver",{value:{receiver:e.receiver}}),e)))}function S(e){if("object"==typeof e&&e.RTCPeerConnection&&!("getSenders"in e.RTCPeerConnection.prototype)&&"createDTMFSender"in e.RTCPeerConnection.prototype){const t=function(e,t){return{track:t,get dtmf(){return void 0===this._dtmf&&("audio"===t.kind?this._dtmf=e.createDTMFSender(t):this._dtmf=null),this._dtmf},_pc:e}};if(!e.RTCPeerConnection.prototype.getSenders){e.RTCPeerConnection.prototype.getSenders=function(){return this._senders=this._senders||[],this._senders.slice()};const n=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,r){let i=n.apply(this,arguments);return i||(i=t(this,e),this._senders.push(i)),i};const r=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){r.apply(this,arguments);const t=this._senders.indexOf(e);-1!==t&&this._senders.splice(t,1)}}const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._senders=this._senders||[],n.apply(this,[e]),e.getTracks().forEach((e=>{this._senders.push(t(this,e))}))};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){this._senders=this._senders||[],r.apply(this,[e]),e.getTracks().forEach((e=>{const t=this._senders.find((t=>t.track===e));t&&this._senders.splice(this._senders.indexOf(t),1)}))}}else if("object"==typeof e&&e.RTCPeerConnection&&"getSenders"in e.RTCPeerConnection.prototype&&"createDTMFSender"in e.RTCPeerConnection.prototype&&e.RTCRtpSender&&!("dtmf"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach((e=>e._pc=this)),e},Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=this._pc.createDTMFSender(this.track):this._dtmf=null),this._dtmf}})}}function k(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,n,r]=arguments;if(arguments.length>0&&"function"==typeof e)return t.apply(this,arguments);if(0===t.length&&(0===arguments.length||"function"!=typeof e))return t.apply(this,[]);const i=function(e){const t={};return e.result().forEach((e=>{const n={id:e.id,timestamp:e.timestamp,type:{localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[e.type]||e.type};e.names().forEach((t=>{n[t]=e.stat(t)})),t[n.id]=n})),t},o=function(e){return new Map(Object.keys(e).map((t=>[t,e[t]])))};if(arguments.length>=2){const r=function(e){n(o(i(e)))};return t.apply(this,[r,e])}return new Promise(((e,n)=>{t.apply(this,[function(t){e(o(i(t)))},n])})).then(n,r)}}function T(e){if(!("object"==typeof e&&e.RTCPeerConnection&&e.RTCRtpSender&&e.RTCRtpReceiver))return;if(!("getStats"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach((e=>e._pc=this)),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){const e=this;return this._pc.getStats().then((t=>f(t,e.track,!0)))}}if(!("getStats"in e.RTCRtpReceiver.prototype)){const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach((e=>e._pc=this)),e}),c(e,"track",(e=>(e.receiver._pc=e.srcElement,e))),e.RTCRtpReceiver.prototype.getStats=function(){const e=this;return this._pc.getStats().then((t=>f(t,e.track,!1)))}}if(!("getStats"in e.RTCRtpSender.prototype)||!("getStats"in e.RTCRtpReceiver.prototype))return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){if(arguments.length>0&&arguments[0]instanceof e.MediaStreamTrack){const e=arguments[0];let t,n,r;return this.getSenders().forEach((n=>{n.track===e&&(t?r=!0:t=n)})),this.getReceivers().forEach((t=>(t.track===e&&(n?r=!0:n=t),t.track===e))),r||t&&n?Promise.reject(new DOMException("There are more than one sender or receiver for the track.","InvalidAccessError")):t?t.getStats():n?n.getStats():Promise.reject(new DOMException("There is no sender or receiver for the track.","InvalidAccessError"))}return t.apply(this,arguments)}}function E(e){e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},Object.keys(this._shimmedLocalStreams).map((e=>this._shimmedLocalStreams[e][0]))};const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,n){if(!n)return t.apply(this,arguments);this._shimmedLocalStreams=this._shimmedLocalStreams||{};const r=t.apply(this,arguments);return this._shimmedLocalStreams[n.id]?-1===this._shimmedLocalStreams[n.id].indexOf(r)&&this._shimmedLocalStreams[n.id].push(r):this._shimmedLocalStreams[n.id]=[n,r],r};const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._shimmedLocalStreams=this._shimmedLocalStreams||{},e.getTracks().forEach((e=>{if(this.getSenders().find((t=>t.track===e)))throw new DOMException("Track already exists.","InvalidAccessError")}));const t=this.getSenders();n.apply(this,arguments);const r=this.getSenders().filter((e=>-1===t.indexOf(e)));this._shimmedLocalStreams[e.id]=[e].concat(r)};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},delete this._shimmedLocalStreams[e.id],r.apply(this,arguments)};const i=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},e&&Object.keys(this._shimmedLocalStreams).forEach((t=>{const n=this._shimmedLocalStreams[t].indexOf(e);-1!==n&&this._shimmedLocalStreams[t].splice(n,1),1===this._shimmedLocalStreams[t].length&&delete this._shimmedLocalStreams[t]})),i.apply(this,arguments)}}function P(e,t){if(!e.RTCPeerConnection)return;if(e.RTCPeerConnection.prototype.addTrack&&t.version>=65)return E(e);const n=e.RTCPeerConnection.prototype.getLocalStreams;e.RTCPeerConnection.prototype.getLocalStreams=function(){const e=n.apply(this);return this._reverseStreams=this._reverseStreams||{},e.map((e=>this._reverseStreams[e.id]))};const r=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(t){if(this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},t.getTracks().forEach((e=>{if(this.getSenders().find((t=>t.track===e)))throw new DOMException("Track already exists.","InvalidAccessError")})),!this._reverseStreams[t.id]){const n=new e.MediaStream(t.getTracks());this._streams[t.id]=n,this._reverseStreams[n.id]=t,t=n}r.apply(this,[t])};const i=e.RTCPeerConnection.prototype.removeStream;function o(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach((t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(i.id,"g"),r.id)})),new RTCSessionDescription({type:t.type,sdp:n})}e.RTCPeerConnection.prototype.removeStream=function(e){this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},i.apply(this,[this._streams[e.id]||e]),delete this._reverseStreams[this._streams[e.id]?this._streams[e.id].id:e.id],delete this._streams[e.id]},e.RTCPeerConnection.prototype.addTrack=function(t,n){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");const r=[].slice.call(arguments,1);if(1!==r.length||!r[0].getTracks().find((e=>e===t)))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(this.getSenders().find((e=>e.track===t)))throw new DOMException("Track already exists.","InvalidAccessError");this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{};const i=this._streams[n.id];if(i)i.addTrack(t),Promise.resolve().then((()=>{this.dispatchEvent(new Event("negotiationneeded"))}));else{const r=new e.MediaStream([t]);this._streams[n.id]=r,this._reverseStreams[r.id]=n,this.addStream(r)}return this.getSenders().find((e=>e.track===t))},["createOffer","createAnswer"].forEach((function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){const e=arguments;return arguments.length&&"function"==typeof arguments[0]?n.apply(this,[t=>{const n=o(this,t);e[0].apply(null,[n])},t=>{e[1]&&e[1].apply(null,t)},arguments[2]]):n.apply(this,arguments).then((e=>o(this,e)))}};e.RTCPeerConnection.prototype[t]=r[t]}));const s=e.RTCPeerConnection.prototype.setLocalDescription;e.RTCPeerConnection.prototype.setLocalDescription=function(){return arguments.length&&arguments[0].type?(arguments[0]=function(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach((t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(r.id,"g"),i.id)})),new RTCSessionDescription({type:t.type,sdp:n})}(this,arguments[0]),s.apply(this,arguments)):s.apply(this,arguments)};const a=Object.getOwnPropertyDescriptor(e.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(e.RTCPeerConnection.prototype,"localDescription",{get(){const e=a.get.apply(this);return""===e.type?e:o(this,e)}}),e.RTCPeerConnection.prototype.removeTrack=function(e){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!e._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(!(e._pc===this))throw new DOMException("Sender was not created by this connection.","InvalidAccessError");let t;this._streams=this._streams||{},Object.keys(this._streams).forEach((n=>{this._streams[n].getTracks().find((t=>e.track===t))&&(t=this._streams[n])})),t&&(1===t.getTracks().length?this.removeStream(this._reverseStreams[t.id]):t.removeTrack(e.track),this.dispatchEvent(new Event("negotiationneeded")))}}function R(e,t){!e.RTCPeerConnection&&e.webkitRTCPeerConnection&&(e.RTCPeerConnection=e.webkitRTCPeerConnection),e.RTCPeerConnection&&t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach((function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]}))}function w(e,t){c(e,"negotiationneeded",(e=>{const n=e.target;if(!(t.version<72||n.getConfiguration&&"plan-b"===n.getConfiguration().sdpSemantics)||"stable"===n.signalingState)return e}))}function A(e,t){const n=e&&e.navigator,r=e&&e.MediaStreamTrack;if(n.getUserMedia=function(e,t,r){p("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(t,r)},!(t.version>55&&"autoGainControl"in n.mediaDevices.getSupportedConstraints())){const e=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])},t=n.mediaDevices.getUserMedia.bind(n.mediaDevices);if(n.mediaDevices.getUserMedia=function(n){return"object"==typeof n&&"object"==typeof n.audio&&(n=JSON.parse(JSON.stringify(n)),e(n.audio,"autoGainControl","mozAutoGainControl"),e(n.audio,"noiseSuppression","mozNoiseSuppression")),t(n)},r&&r.prototype.getSettings){const t=r.prototype.getSettings;r.prototype.getSettings=function(){const n=t.apply(this,arguments);return e(n,"mozAutoGainControl","autoGainControl"),e(n,"mozNoiseSuppression","noiseSuppression"),n}}if(r&&r.prototype.applyConstraints){const t=r.prototype.applyConstraints;r.prototype.applyConstraints=function(n){return"audio"===this.kind&&"object"==typeof n&&(n=JSON.parse(JSON.stringify(n)),e(n,"autoGainControl","mozAutoGainControl"),e(n,"noiseSuppression","mozNoiseSuppression")),t.apply(this,[n])}}}}function x(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&(e.navigator.mediaDevices.getDisplayMedia=function(n){if(!n||!n.video){const e=new DOMException("getDisplayMedia without video constraints is undefined");return e.name="NotFoundError",e.code=8,Promise.reject(e)}return!0===n.video?n.video={mediaSource:t}:n.video.mediaSource=t,e.navigator.mediaDevices.getUserMedia(n)})}function I(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function O(e,t){if("object"!=typeof e||!e.RTCPeerConnection&&!e.mozRTCPeerConnection)return;!e.RTCPeerConnection&&e.mozRTCPeerConnection&&(e.RTCPeerConnection=e.mozRTCPeerConnection),t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach((function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]}));const n={inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"},r=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,i,o]=arguments;return r.apply(this,[e||null]).then((e=>{if(t.version<53&&!i)try{e.forEach((e=>{e.type=n[e.type]||e.type}))}catch(t){if("TypeError"!==t.name)throw t;e.forEach(((t,r)=>{e.set(r,Object.assign({},t,{type:n[t.type]||t.type}))}))}return e})).then(i,o)}}function D(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpSender.prototype)return;const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach((e=>e._pc=this)),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){return this.track?this._pc.getStats(this.track):Promise.resolve(new Map)}}function M(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpReceiver.prototype)return;const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach((e=>e._pc=this)),e}),c(e,"track",(e=>(e.receiver._pc=e.srcElement,e))),e.RTCRtpReceiver.prototype.getStats=function(){return this._pc.getStats(this.track)}}function L(e){e.RTCPeerConnection&&!("removeStream"in e.RTCPeerConnection.prototype)&&(e.RTCPeerConnection.prototype.removeStream=function(e){p("removeStream","removeTrack"),this.getSenders().forEach((t=>{t.track&&e.getTracks().includes(t.track)&&this.removeTrack(t)}))})}function H(e){e.DataChannel&&!e.RTCDataChannel&&(e.RTCDataChannel=e.DataChannel)}function G(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.addTransceiver;t&&(e.RTCPeerConnection.prototype.addTransceiver=function(){this.setParametersPromises=[];let e=arguments[1]&&arguments[1].sendEncodings;void 0===e&&(e=[]),e=[...e];const n=e.length>0;n&&e.forEach((e=>{if("rid"in e){if(!/^[a-z0-9]{0,16}$/i.test(e.rid))throw new TypeError("Invalid RID value provided.")}if("scaleResolutionDownBy"in e&&!(parseFloat(e.scaleResolutionDownBy)>=1))throw new RangeError("scale_resolution_down_by must be >= 1.0");if("maxFramerate"in e&&!(parseFloat(e.maxFramerate)>=0))throw new RangeError("max_framerate must be >= 0.0")}));const r=t.apply(this,arguments);if(n){const{sender:t}=r,n=t.getParameters();(!("encodings"in n)||1===n.encodings.length&&0===Object.keys(n.encodings[0]).length)&&(n.encodings=e,t.sendEncodings=e,this.setParametersPromises.push(t.setParameters(n).then((()=>{delete t.sendEncodings})).catch((()=>{delete t.sendEncodings}))))}return r})}function j(e){if("object"!=typeof e||!e.RTCRtpSender)return;const t=e.RTCRtpSender.prototype.getParameters;t&&(e.RTCRtpSender.prototype.getParameters=function(){const e=t.apply(this,arguments);return"encodings"in e||(e.encodings=[].concat(this.sendEncodings||[{}])),e})}function N(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then((()=>t.apply(this,arguments))).finally((()=>{this.setParametersPromises=[]})):t.apply(this,arguments)}}function U(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createAnswer;e.RTCPeerConnection.prototype.createAnswer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then((()=>t.apply(this,arguments))).finally((()=>{this.setParametersPromises=[]})):t.apply(this,arguments)}}function F(e){if("object"==typeof e&&e.RTCPeerConnection){if("getLocalStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),!("addStream"in e.RTCPeerConnection.prototype)){const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addStream=function(e){this._localStreams||(this._localStreams=[]),this._localStreams.includes(e)||this._localStreams.push(e),e.getAudioTracks().forEach((n=>t.call(this,n,e))),e.getVideoTracks().forEach((n=>t.call(this,n,e)))},e.RTCPeerConnection.prototype.addTrack=function(e,...n){return n&&n.forEach((e=>{this._localStreams?this._localStreams.includes(e)||this._localStreams.push(e):this._localStreams=[e]})),t.apply(this,arguments)}}"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(e){this._localStreams||(this._localStreams=[]);const t=this._localStreams.indexOf(e);if(-1===t)return;this._localStreams.splice(t,1);const n=e.getTracks();this.getSenders().forEach((e=>{n.includes(e.track)&&this.removeTrack(e)}))})}}function z(e){if("object"==typeof e&&e.RTCPeerConnection&&("getRemoteStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),!("onaddstream"in e.RTCPeerConnection.prototype))){Object.defineProperty(e.RTCPeerConnection.prototype,"onaddstream",{get(){return this._onaddstream},set(e){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=e),this.addEventListener("track",this._onaddstreampoly=e=>{e.streams.forEach((e=>{if(this._remoteStreams||(this._remoteStreams=[]),this._remoteStreams.includes(e))return;this._remoteStreams.push(e);const t=new Event("addstream");t.stream=e,this.dispatchEvent(t)}))})}});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){const e=this;return this._onaddstreampoly||this.addEventListener("track",this._onaddstreampoly=function(t){t.streams.forEach((t=>{if(e._remoteStreams||(e._remoteStreams=[]),e._remoteStreams.indexOf(t)>=0)return;e._remoteStreams.push(t);const n=new Event("addstream");n.stream=t,e.dispatchEvent(n)}))}),t.apply(e,arguments)}}}function J(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype,n=t.createOffer,r=t.createAnswer,i=t.setLocalDescription,o=t.setRemoteDescription,s=t.addIceCandidate;t.createOffer=function(e,t){const r=arguments.length>=2?arguments[2]:arguments[0],i=n.apply(this,[r]);return t?(i.then(e,t),Promise.resolve()):i},t.createAnswer=function(e,t){const n=arguments.length>=2?arguments[2]:arguments[0],i=r.apply(this,[n]);return t?(i.then(e,t),Promise.resolve()):i};let a=function(e,t,n){const r=i.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r};t.setLocalDescription=a,a=function(e,t,n){const r=o.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.setRemoteDescription=a,a=function(e,t,n){const r=s.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.addIceCandidate=a}function K(e){const t=e&&e.navigator;if(t.mediaDevices&&t.mediaDevices.getUserMedia){const e=t.mediaDevices,n=e.getUserMedia.bind(e);t.mediaDevices.getUserMedia=e=>n(q(e))}!t.getUserMedia&&t.mediaDevices&&t.mediaDevices.getUserMedia&&(t.getUserMedia=function(e,n,r){t.mediaDevices.getUserMedia(e).then(n,r)}.bind(t))}function q(e){return e&&void 0!==e.video?Object.assign({},e,{video:m(e.video)}):e}function W(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection;e.RTCPeerConnection=function(e,n){if(e&&e.iceServers){const t=[];for(let n=0;nt.generateCertificate})}function Y(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function V(e){const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(e){if(e){void 0!==e.offerToReceiveAudio&&(e.offerToReceiveAudio=!!e.offerToReceiveAudio);const t=this.getTransceivers().find((e=>"audio"===e.receiver.track.kind));!1===e.offerToReceiveAudio&&t?"sendrecv"===t.direction?t.setDirection?t.setDirection("sendonly"):t.direction="sendonly":"recvonly"===t.direction&&(t.setDirection?t.setDirection("inactive"):t.direction="inactive"):!0!==e.offerToReceiveAudio||t||this.addTransceiver("audio",{direction:"recvonly"}),void 0!==e.offerToReceiveVideo&&(e.offerToReceiveVideo=!!e.offerToReceiveVideo);const n=this.getTransceivers().find((e=>"video"===e.receiver.track.kind));!1===e.offerToReceiveVideo&&n?"sendrecv"===n.direction?n.setDirection?n.setDirection("sendonly"):n.direction="sendonly":"recvonly"===n.direction&&(n.setDirection?n.setDirection("inactive"):n.direction="inactive"):!0!==e.offerToReceiveVideo||n||this.addTransceiver("video",{direction:"recvonly"})}return t.apply(this,arguments)}}function B(e){"object"!=typeof e||e.AudioContext||(e.AudioContext=e.webkitAudioContext)}var Z=n(539),X=n.n(Z);function $(e){if(!e.RTCIceCandidate||e.RTCIceCandidate&&"foundation"in e.RTCIceCandidate.prototype)return;const t=e.RTCIceCandidate;e.RTCIceCandidate=function(e){if("object"==typeof e&&e.candidate&&0===e.candidate.indexOf("a=")&&((e=JSON.parse(JSON.stringify(e))).candidate=e.candidate.substring(2)),e.candidate&&e.candidate.length){const n=new t(e),r=X().parseCandidate(e.candidate);for(const e in r)e in n||Object.defineProperty(n,e,{value:r[e]});return n.toJSON=function(){return{candidate:n.candidate,sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex,usernameFragment:n.usernameFragment}},n}return new t(e)},e.RTCIceCandidate.prototype=t.prototype,c(e,"icecandidate",(t=>(t.candidate&&Object.defineProperty(t,"candidate",{value:new e.RTCIceCandidate(t.candidate),writable:"false"}),t)))}function Q(e){!e.RTCIceCandidate||e.RTCIceCandidate&&"relayProtocol"in e.RTCIceCandidate.prototype||c(e,"icecandidate",(e=>{if(e.candidate){const t=X().parseCandidate(e.candidate.candidate);"relay"===t.type&&(e.candidate.relayProtocol={0:"tls",1:"tcp",2:"udp"}[t.priority>>24])}return e}))}function ee(e,t){if(!e.RTCPeerConnection)return;"sctp"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"sctp",{get(){return void 0===this._sctp?null:this._sctp}});const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){if(this._sctp=null,"chrome"===t.browser&&t.version>=76){const{sdpSemantics:e}=this.getConfiguration();"plan-b"===e&&Object.defineProperty(this,"sctp",{get(){return void 0===this._sctp?null:this._sctp},enumerable:!0,configurable:!0})}if(function(e){if(!e||!e.sdp)return!1;const t=X().splitSections(e.sdp);return t.shift(),t.some((e=>{const t=X().parseMLine(e);return t&&"application"===t.kind&&-1!==t.protocol.indexOf("SCTP")}))}(arguments[0])){const e=function(e){const t=e.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);if(null===t||t.length<2)return-1;const n=parseInt(t[1],10);return n!=n?-1:n}(arguments[0]),n=function(e){let n=65536;return"firefox"===t.browser&&(n=t.version<57?-1===e?16384:2147483637:t.version<60?57===t.version?65535:65536:2147483637),n}(e),r=function(e,n){let r=65536;"firefox"===t.browser&&57===t.version&&(r=65535);const i=X().matchPrefix(e.sdp,"a=max-message-size:");return i.length>0?r=parseInt(i[0].substring(19),10):"firefox"===t.browser&&-1!==n&&(r=2147483637),r}(arguments[0],e);let i;i=0===n&&0===r?Number.POSITIVE_INFINITY:0===n||0===r?Math.max(n,r):Math.min(n,r);const o={};Object.defineProperty(o,"maxMessageSize",{get:()=>i}),this._sctp=o}return n.apply(this,arguments)}}function te(e){if(!e.RTCPeerConnection||!("createDataChannel"in e.RTCPeerConnection.prototype))return;function t(e,t){const n=e.send;e.send=function(){const r=arguments[0],i=r.length||r.size||r.byteLength;if("open"===e.readyState&&t.sctp&&i>t.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+t.sctp.maxMessageSize+" bytes)");return n.apply(e,arguments)}}const n=e.RTCPeerConnection.prototype.createDataChannel;e.RTCPeerConnection.prototype.createDataChannel=function(){const e=n.apply(this,arguments);return t(e,this),e},c(e,"datachannel",(e=>(t(e.channel,e.target),e)))}function ne(e){if(!e.RTCPeerConnection||"connectionState"in e.RTCPeerConnection.prototype)return;const t=e.RTCPeerConnection.prototype;Object.defineProperty(t,"connectionState",{get(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(t,"onconnectionstatechange",{get(){return this._onconnectionstatechange||null},set(e){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),e&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=e)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach((e=>{const n=t[e];t[e]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=e=>{const t=e.target;if(t._lastConnectionState!==t.connectionState){t._lastConnectionState=t.connectionState;const n=new Event("connectionstatechange",e);t.dispatchEvent(n)}return e},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),n.apply(this,arguments)}}))}function re(e,t){if(!e.RTCPeerConnection)return;if("chrome"===t.browser&&t.version>=71)return;if("safari"===t.browser&&t.version>=605)return;const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(t){if(t&&t.sdp&&-1!==t.sdp.indexOf("\na=extmap-allow-mixed")){const n=t.sdp.split("\n").filter((e=>"a=extmap-allow-mixed"!==e.trim())).join("\n");e.RTCSessionDescription&&t instanceof e.RTCSessionDescription?arguments[0]=new e.RTCSessionDescription({type:t.type,sdp:n}):t.sdp=n}return n.apply(this,arguments)}}function ie(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.addIceCandidate;n&&0!==n.length&&(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?("chrome"===t.browser&&t.version<78||"firefox"===t.browser&&t.version<68||"safari"===t.browser)&&arguments[0]&&""===arguments[0].candidate?Promise.resolve():n.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function oe(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.setLocalDescription;n&&0!==n.length&&(e.RTCPeerConnection.prototype.setLocalDescription=function(){let e=arguments[0]||{};if("object"!=typeof e||e.type&&e.sdp)return n.apply(this,arguments);if(e={type:e.type,sdp:e.sdp},!e.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":e.type="offer";break;default:e.type="answer"}if(e.sdp||"offer"!==e.type&&"answer"!==e.type)return n.apply(this,[e]);return("offer"===e.type?this.createOffer:this.createAnswer).apply(this).then((e=>n.apply(this,[e])))})}!function({window:n}={},o={shimChrome:!0,shimFirefox:!0,shimSafari:!0}){const s=h,c=function(e){const t={browser:null,version:null};if(void 0===e||!e.navigator)return t.browser="Not a browser.",t;const{navigator:n}=e;if(n.mozGetUserMedia)t.browser="firefox",t.version=a(n.userAgent,/Firefox\/(\d+)\./,1);else if(n.webkitGetUserMedia||!1===e.isSecureContext&&e.webkitRTCPeerConnection)t.browser="chrome",t.version=a(n.userAgent,/Chrom(e|ium)\/(\d+)\./,2);else{if(!e.RTCPeerConnection||!n.userAgent.match(/AppleWebKit\/(\d+)\./))return t.browser="Not a supported browser.",t;t.browser="safari",t.version=a(n.userAgent,/AppleWebKit\/(\d+)\./,1),t.supportsUnifiedPlan=e.RTCRtpTransceiver&&"currentDirection"in e.RTCRtpTransceiver.prototype}return t}(n),p={browserDetails:c,commonShim:i,extractVersion:a,disableLog:d,disableWarnings:l,sdp:Z};switch(c.browser){case"chrome":if(!e||!R||!o.shimChrome)return s("Chrome shim is not included in this adapter release."),p;if(null===c.version)return s("Chrome shim can not determine version, not shimming."),p;s("adapter.js shimming chrome."),p.browserShim=e,ie(n,c),oe(n),C(n,c),v(n),R(n,c),b(n),P(n,c),S(n),k(n),T(n),w(n,c),$(n),Q(n),ne(n),ee(n,c),te(n),re(n,c);break;case"firefox":if(!t||!O||!o.shimFirefox)return s("Firefox shim is not included in this adapter release."),p;s("adapter.js shimming firefox."),p.browserShim=t,ie(n,c),oe(n),A(n,c),O(n,c),I(n),L(n),D(n),M(n),H(n),G(n),j(n),N(n),U(n),$(n),ne(n),ee(n,c),te(n);break;case"safari":if(!r||!o.shimSafari)return s("Safari shim is not included in this adapter release."),p;s("adapter.js shimming safari."),p.browserShim=r,ie(n,c),oe(n),W(n),V(n),J(n),F(n),z(n),Y(n),K(n),B(n),$(n),Q(n),ee(n,c),te(n),re(n,c);break;default:s("Unsupported browser!")}}({window:"undefined"==typeof window?void 0:window});const se=Object.freeze({meta:null,signalingServerUrl:"ws://127.0.0.1:8443",reconnectionTimeout:2500,webrtcConfig:{iceServers:[{urls:["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302"]}]}}),ae={idle:0,connecting:1,streaming:2,closed:3};Object.freeze(ae);const ce=ae;class de extends EventTarget{constructor(e,t){super(),this._peerId=e,this._sessionId="",this._comChannel=t,this._state=ce.idle,this._rtcPeerConnection=null}get peerId(){return this._peerId}get sessionId(){return this._sessionId}get state(){return this._state}get rtcPeerConnection(){return this._rtcPeerConnection}close(){this._state!==ce.closed&&(this._state!==ce.idle&&this._comChannel&&this._sessionId&&this._comChannel.send({type:"endSession",sessionId:this._sessionId}),this._state=ce.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._rtcPeerConnection&&(this._rtcPeerConnection.close(),this._rtcPeerConnection=null,this.dispatchEvent(new Event("rtcPeerConnectionChanged"))),this.dispatchEvent(new Event("closed")))}}const le=de,he=Object.freeze({32:"space",33:"exclam",34:"quotedbl",35:"numbersign",36:"dollar",37:"percent",38:"ampersand",39:"apostrophe",40:"parenleft",41:"parenright",42:"asterisk",43:"plus",44:"comma",45:"minus",46:"period",47:"slash",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",58:"colon",59:"semicolon",60:"less",61:"equal",62:"greater",63:"question",64:"at",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",91:"bracketleft",92:"backslash",93:"bracketright",94:"asciicircum",95:"underscore",96:"grave",97:"a",98:"b",99:"c",100:"d",101:"e",102:"f",103:"g",104:"h",105:"i",106:"j",107:"k",108:"l",109:"m",110:"n",111:"o",112:"p",113:"q",114:"r",115:"s",116:"t",117:"u",118:"v",119:"w",120:"x",121:"y",122:"z",123:"braceleft",124:"bar",125:"braceright",126:"asciitilde",160:"nobreakspace",161:"exclamdown",162:"cent",163:"sterling",164:"currency",165:"yen",166:"brokenbar",167:"section",168:"diaeresis",169:"copyright",170:"ordfeminine",171:"guillemotleft",172:"notsign",173:"hyphen",174:"registered",175:"macron",176:"degree",177:"plusminus",178:"twosuperior",179:"threesuperior",180:"acute",181:"mu",182:"paragraph",183:"periodcentered",184:"cedilla",185:"onesuperior",186:"masculine",187:"guillemotright",188:"onequarter",189:"onehalf",190:"threequarters",191:"questiondown",192:"Agrave",193:"Aacute",194:"Acircumflex",195:"Atilde",196:"Adiaeresis",197:"Aring",198:"AE",199:"Ccedilla",200:"Egrave",201:"Eacute",202:"Ecircumflex",203:"Ediaeresis",204:"Igrave",205:"Iacute",206:"Icircumflex",207:"Idiaeresis",208:"ETH",209:"Ntilde",210:"Ograve",211:"Oacute",212:"Ocircumflex",213:"Otilde",214:"Odiaeresis",215:"multiply",216:"Ooblique",217:"Ugrave",218:"Uacute",219:"Ucircumflex",220:"Udiaeresis",221:"Yacute",222:"THORN",223:"ssharp",224:"agrave",225:"aacute",226:"acircumflex",227:"atilde",228:"adiaeresis",229:"aring",230:"ae",231:"ccedilla",232:"egrave",233:"eacute",234:"ecircumflex",235:"ediaeresis",236:"igrave",237:"iacute",238:"icircumflex",239:"idiaeresis",240:"eth",241:"ntilde",242:"ograve",243:"oacute",244:"ocircumflex",245:"otilde",246:"odiaeresis",247:"division",248:"oslash",249:"ugrave",250:"uacute",251:"ucircumflex",252:"udiaeresis",253:"yacute",254:"thorn",255:"ydiaeresis",260:"Aogonek",728:"breve",321:"Lstroke",317:"Lcaron",346:"Sacute",352:"Scaron",350:"Scedilla",356:"Tcaron",377:"Zacute",381:"Zcaron",379:"Zabovedot",261:"aogonek",731:"ogonek",322:"lstroke",318:"lcaron",347:"sacute",711:"caron",353:"scaron",351:"scedilla",357:"tcaron",378:"zacute",733:"doubleacute",382:"zcaron",380:"zabovedot",340:"Racute",258:"Abreve",313:"Lacute",262:"Cacute",268:"Ccaron",280:"Eogonek",282:"Ecaron",270:"Dcaron",272:"Dstroke",323:"Nacute",327:"Ncaron",336:"Odoubleacute",344:"Rcaron",366:"Uring",368:"Udoubleacute",354:"Tcedilla",341:"racute",259:"abreve",314:"lacute",263:"cacute",269:"ccaron",281:"eogonek",283:"ecaron",271:"dcaron",273:"dstroke",324:"nacute",328:"ncaron",337:"odoubleacute",345:"rcaron",367:"uring",369:"udoubleacute",355:"tcedilla",729:"abovedot",294:"Hstroke",292:"Hcircumflex",304:"Iabovedot",286:"Gbreve",308:"Jcircumflex",295:"hstroke",293:"hcircumflex",305:"idotless",287:"gbreve",309:"jcircumflex",266:"Cabovedot",264:"Ccircumflex",288:"Gabovedot",284:"Gcircumflex",364:"Ubreve",348:"Scircumflex",267:"cabovedot",265:"ccircumflex",289:"gabovedot",285:"gcircumflex",365:"ubreve",349:"scircumflex",312:"kra",342:"Rcedilla",296:"Itilde",315:"Lcedilla",274:"Emacron",290:"Gcedilla",358:"Tslash",343:"rcedilla",297:"itilde",316:"lcedilla",275:"emacron",291:"gcedilla",359:"tslash",330:"ENG",331:"eng",256:"Amacron",302:"Iogonek",278:"Eabovedot",298:"Imacron",325:"Ncedilla",332:"Omacron",310:"Kcedilla",370:"Uogonek",360:"Utilde",362:"Umacron",257:"amacron",303:"iogonek",279:"eabovedot",299:"imacron",326:"ncedilla",333:"omacron",311:"kcedilla",371:"uogonek",361:"utilde",363:"umacron",8254:"overline",12290:"kana_fullstop",12300:"kana_openingbracket",12301:"kana_closingbracket",12289:"kana_comma",12539:"kana_conjunctive",12530:"kana_WO",12449:"kana_a",12451:"kana_i",12453:"kana_u",12455:"kana_e",12457:"kana_o",12515:"kana_ya",12517:"kana_yu",12519:"kana_yo",12483:"kana_tsu",12540:"prolongedsound",12450:"kana_A",12452:"kana_I",12454:"kana_U",12456:"kana_E",12458:"kana_O",12459:"kana_KA",12461:"kana_KI",12463:"kana_KU",12465:"kana_KE",12467:"kana_KO",12469:"kana_SA",12471:"kana_SHI",12473:"kana_SU",12475:"kana_SE",12477:"kana_SO",12479:"kana_TA",12481:"kana_CHI",12484:"kana_TSU",12486:"kana_TE",12488:"kana_TO",12490:"kana_NA",12491:"kana_NI",12492:"kana_NU",12493:"kana_NE",12494:"kana_NO",12495:"kana_HA",12498:"kana_HI",12501:"kana_FU",12504:"kana_HE",12507:"kana_HO",12510:"kana_MA",12511:"kana_MI",12512:"kana_MU",12513:"kana_ME",12514:"kana_MO",12516:"kana_YA",12518:"kana_YU",12520:"kana_YO",12521:"kana_RA",12522:"kana_RI",12523:"kana_RU",12524:"kana_RE",12525:"kana_RO",12527:"kana_WA",12531:"kana_N",12443:"voicedsound",12444:"semivoicedsound",1548:"Arabic_comma",1563:"Arabic_semicolon",1567:"Arabic_question_mark",1569:"Arabic_hamza",1570:"Arabic_maddaonalef",1571:"Arabic_hamzaonalef",1572:"Arabic_hamzaonwaw",1573:"Arabic_hamzaunderalef",1574:"Arabic_hamzaonyeh",1575:"Arabic_alef",1576:"Arabic_beh",1577:"Arabic_tehmarbuta",1578:"Arabic_teh",1579:"Arabic_theh",1580:"Arabic_jeem",1581:"Arabic_hah",1582:"Arabic_khah",1583:"Arabic_dal",1584:"Arabic_thal",1585:"Arabic_ra",1586:"Arabic_zain",1587:"Arabic_seen",1588:"Arabic_sheen",1589:"Arabic_sad",1590:"Arabic_dad",1591:"Arabic_tah",1592:"Arabic_zah",1593:"Arabic_ain",1594:"Arabic_ghain",1600:"Arabic_tatweel",1601:"Arabic_feh",1602:"Arabic_qaf",1603:"Arabic_kaf",1604:"Arabic_lam",1605:"Arabic_meem",1606:"Arabic_noon",1607:"Arabic_ha",1608:"Arabic_waw",1609:"Arabic_alefmaksura",1610:"Arabic_yeh",1611:"Arabic_fathatan",1612:"Arabic_dammatan",1613:"Arabic_kasratan",1614:"Arabic_fatha",1615:"Arabic_damma",1616:"Arabic_kasra",1617:"Arabic_shadda",1618:"Arabic_sukun",1106:"Serbian_dje",1107:"Macedonia_gje",1105:"Cyrillic_io",1108:"Ukrainian_ie",1109:"Macedonia_dse",1110:"Ukrainian_i",1111:"Ukrainian_yi",1112:"Cyrillic_je",1113:"Cyrillic_lje",1114:"Cyrillic_nje",1115:"Serbian_tshe",1116:"Macedonia_kje",1118:"Byelorussian_shortu",1119:"Cyrillic_dzhe",8470:"numerosign",1026:"Serbian_DJE",1027:"Macedonia_GJE",1025:"Cyrillic_IO",1028:"Ukrainian_IE",1029:"Macedonia_DSE",1030:"Ukrainian_I",1031:"Ukrainian_YI",1032:"Cyrillic_JE",1033:"Cyrillic_LJE",1034:"Cyrillic_NJE",1035:"Serbian_TSHE",1036:"Macedonia_KJE",1038:"Byelorussian_SHORTU",1039:"Cyrillic_DZHE",1102:"Cyrillic_yu",1072:"Cyrillic_a",1073:"Cyrillic_be",1094:"Cyrillic_tse",1076:"Cyrillic_de",1077:"Cyrillic_ie",1092:"Cyrillic_ef",1075:"Cyrillic_ghe",1093:"Cyrillic_ha",1080:"Cyrillic_i",1081:"Cyrillic_shorti",1082:"Cyrillic_ka",1083:"Cyrillic_el",1084:"Cyrillic_em",1085:"Cyrillic_en",1086:"Cyrillic_o",1087:"Cyrillic_pe",1103:"Cyrillic_ya",1088:"Cyrillic_er",1089:"Cyrillic_es",1090:"Cyrillic_te",1091:"Cyrillic_u",1078:"Cyrillic_zhe",1074:"Cyrillic_ve",1100:"Cyrillic_softsign",1099:"Cyrillic_yeru",1079:"Cyrillic_ze",1096:"Cyrillic_sha",1101:"Cyrillic_e",1097:"Cyrillic_shcha",1095:"Cyrillic_che",1098:"Cyrillic_hardsign",1070:"Cyrillic_YU",1040:"Cyrillic_A",1041:"Cyrillic_BE",1062:"Cyrillic_TSE",1044:"Cyrillic_DE",1045:"Cyrillic_IE",1060:"Cyrillic_EF",1043:"Cyrillic_GHE",1061:"Cyrillic_HA",1048:"Cyrillic_I",1049:"Cyrillic_SHORTI",1050:"Cyrillic_KA",1051:"Cyrillic_EL",1052:"Cyrillic_EM",1053:"Cyrillic_EN",1054:"Cyrillic_O",1055:"Cyrillic_PE",1071:"Cyrillic_YA",1056:"Cyrillic_ER",1057:"Cyrillic_ES",1058:"Cyrillic_TE",1059:"Cyrillic_U",1046:"Cyrillic_ZHE",1042:"Cyrillic_VE",1068:"Cyrillic_SOFTSIGN",1067:"Cyrillic_YERU",1047:"Cyrillic_ZE",1064:"Cyrillic_SHA",1069:"Cyrillic_E",1065:"Cyrillic_SHCHA",1063:"Cyrillic_CHE",1066:"Cyrillic_HARDSIGN",902:"Greek_ALPHAaccent",904:"Greek_EPSILONaccent",905:"Greek_ETAaccent",906:"Greek_IOTAaccent",938:"Greek_IOTAdiaeresis",908:"Greek_OMICRONaccent",910:"Greek_UPSILONaccent",939:"Greek_UPSILONdieresis",911:"Greek_OMEGAaccent",901:"Greek_accentdieresis",8213:"Greek_horizbar",940:"Greek_alphaaccent",941:"Greek_epsilonaccent",942:"Greek_etaaccent",943:"Greek_iotaaccent",970:"Greek_iotadieresis",912:"Greek_iotaaccentdieresis",972:"Greek_omicronaccent",973:"Greek_upsilonaccent",971:"Greek_upsilondieresis",944:"Greek_upsilonaccentdieresis",974:"Greek_omegaaccent",913:"Greek_ALPHA",914:"Greek_BETA",915:"Greek_GAMMA",916:"Greek_DELTA",917:"Greek_EPSILON",918:"Greek_ZETA",919:"Greek_ETA",920:"Greek_THETA",921:"Greek_IOTA",922:"Greek_KAPPA",923:"Greek_LAMBDA",924:"Greek_MU",925:"Greek_NU",926:"Greek_XI",927:"Greek_OMICRON",928:"Greek_PI",929:"Greek_RHO",931:"Greek_SIGMA",932:"Greek_TAU",933:"Greek_UPSILON",934:"Greek_PHI",935:"Greek_CHI",936:"Greek_PSI",937:"Greek_OMEGA",945:"Greek_alpha",946:"Greek_beta",947:"Greek_gamma",948:"Greek_delta",949:"Greek_epsilon",950:"Greek_zeta",951:"Greek_eta",952:"Greek_theta",953:"Greek_iota",954:"Greek_kappa",955:"Greek_lambda",956:"Greek_mu",957:"Greek_nu",958:"Greek_xi",959:"Greek_omicron",960:"Greek_pi",961:"Greek_rho",963:"Greek_sigma",962:"Greek_finalsmallsigma",964:"Greek_tau",965:"Greek_upsilon",966:"Greek_phi",967:"Greek_chi",968:"Greek_psi",969:"Greek_omega",9143:"leftradical",8992:"topintegral",8993:"botintegral",9121:"topleftsqbracket",9123:"botleftsqbracket",9124:"toprightsqbracket",9126:"botrightsqbracket",9115:"topleftparens",9117:"botleftparens",9118:"toprightparens",9120:"botrightparens",9128:"leftmiddlecurlybrace",9132:"rightmiddlecurlybrace",8804:"lessthanequal",8800:"notequal",8805:"greaterthanequal",8747:"integral",8756:"therefore",8733:"variation",8734:"infinity",8711:"nabla",8764:"approximate",8771:"similarequal",8660:"ifonlyif",8658:"implies",8801:"identical",8730:"radical",8834:"includedin",8835:"includes",8745:"intersection",8746:"union",8743:"logicaland",8744:"logicalor",8706:"partialderivative",402:"function",8592:"leftarrow",8593:"uparrow",8594:"rightarrow",8595:"downarrow",9670:"soliddiamond",9618:"checkerboard",9225:"ht",9228:"ff",9229:"cr",9226:"lf",9252:"nl",9227:"vt",9496:"lowrightcorner",9488:"uprightcorner",9484:"upleftcorner",9492:"lowleftcorner",9532:"crossinglines",9146:"horizlinescan1",9147:"horizlinescan3",9472:"horizlinescan5",9148:"horizlinescan7",9149:"horizlinescan9",9500:"leftt",9508:"rightt",9524:"bott",9516:"topt",9474:"vertbar",8195:"emspace",8194:"enspace",8196:"em3space",8197:"em4space",8199:"digitspace",8200:"punctspace",8201:"thinspace",8202:"hairspace",8212:"emdash",8211:"endash",9251:"signifblank",8230:"ellipsis",8229:"doubbaselinedot",8531:"onethird",8532:"twothirds",8533:"onefifth",8534:"twofifths",8535:"threefifths",8536:"fourfifths",8537:"onesixth",8538:"fivesixths",8453:"careof",8210:"figdash",10216:"leftanglebracket",10217:"rightanglebracket",8539:"oneeighth",8540:"threeeighths",8541:"fiveeighths",8542:"seveneighths",8482:"trademark",9747:"signaturemark",9665:"leftopentriangle",9655:"rightopentriangle",9647:"emopenrectangle",8216:"leftsinglequotemark",8217:"rightsinglequotemark",8220:"leftdoublequotemark",8221:"rightdoublequotemark",8478:"prescription",8242:"minutes",8243:"seconds",10013:"latincross",9644:"filledrectbullet",9664:"filledlefttribullet",9654:"filledrighttribullet",9679:"emfilledcircle",9646:"emfilledrect",9702:"enopencircbullet",9643:"enopensquarebullet",9645:"openrectbullet",9651:"opentribulletup",9661:"opentribulletdown",9734:"openstar",8226:"enfilledcircbullet",9642:"enfilledsqbullet",9650:"filledtribulletup",9660:"filledtribulletdown",9756:"leftpointer",9758:"rightpointer",9827:"club",9830:"diamond",9829:"heart",10016:"maltesecross",8224:"dagger",8225:"doubledagger",10003:"checkmark",10007:"ballotcross",9839:"musicalsharp",9837:"musicalflat",9794:"malesymbol",9792:"femalesymbol",9742:"telephone",8981:"telephonerecorder",8471:"phonographcopyright",8248:"caret",8218:"singlelowquotemark",8222:"doublelowquotemark",8869:"downtack",8970:"downstile",8728:"jot",9109:"quad",8868:"uptack",9675:"circle",8968:"upstile",8866:"lefttack",8867:"righttack",8215:"hebrew_doublelowline",1488:"hebrew_aleph",1489:"hebrew_beth",1490:"hebrew_gimmel",1491:"hebrew_daleth",1492:"hebrew_he",1493:"hebrew_waw",1494:"hebrew_zayin",1495:"hebrew_het",1496:"hebrew_teth",1497:"hebrew_yod",1498:"hebrew_finalkaph",1499:"hebrew_kaph",1500:"hebrew_lamed",1501:"hebrew_finalmem",1502:"hebrew_mem",1503:"hebrew_finalnun",1504:"hebrew_nun",1505:"hebrew_samekh",1506:"hebrew_ayin",1507:"hebrew_finalpe",1508:"hebrew_pe",1509:"hebrew_finalzadi",1510:"hebrew_zadi",1511:"hebrew_qoph",1512:"hebrew_resh",1513:"hebrew_shin",1514:"hebrew_taw",3585:"Thai_kokai",3586:"Thai_khokhai",3587:"Thai_khokhuat",3588:"Thai_khokhwai",3589:"Thai_khokhon",3590:"Thai_khorakhang",3591:"Thai_ngongu",3592:"Thai_chochan",3593:"Thai_choching",3594:"Thai_chochang",3595:"Thai_soso",3596:"Thai_chochoe",3597:"Thai_yoying",3598:"Thai_dochada",3599:"Thai_topatak",3600:"Thai_thothan",3601:"Thai_thonangmontho",3602:"Thai_thophuthao",3603:"Thai_nonen",3604:"Thai_dodek",3605:"Thai_totao",3606:"Thai_thothung",3607:"Thai_thothahan",3608:"Thai_thothong",3609:"Thai_nonu",3610:"Thai_bobaimai",3611:"Thai_popla",3612:"Thai_phophung",3613:"Thai_fofa",3614:"Thai_phophan",3615:"Thai_fofan",3616:"Thai_phosamphao",3617:"Thai_moma",3618:"Thai_yoyak",3619:"Thai_rorua",3620:"Thai_ru",3621:"Thai_loling",3622:"Thai_lu",3623:"Thai_wowaen",3624:"Thai_sosala",3625:"Thai_sorusi",3626:"Thai_sosua",3627:"Thai_hohip",3628:"Thai_lochula",3629:"Thai_oang",3630:"Thai_honokhuk",3631:"Thai_paiyannoi",3632:"Thai_saraa",3633:"Thai_maihanakat",3634:"Thai_saraaa",3635:"Thai_saraam",3636:"Thai_sarai",3637:"Thai_saraii",3638:"Thai_saraue",3639:"Thai_sarauee",3640:"Thai_sarau",3641:"Thai_sarauu",3642:"Thai_phinthu",3647:"Thai_baht",3648:"Thai_sarae",3649:"Thai_saraae",3650:"Thai_sarao",3651:"Thai_saraaimaimuan",3652:"Thai_saraaimaimalai",3653:"Thai_lakkhangyao",3654:"Thai_maiyamok",3655:"Thai_maitaikhu",3656:"Thai_maiek",3657:"Thai_maitho",3658:"Thai_maitri",3659:"Thai_maichattawa",3660:"Thai_thanthakhat",3661:"Thai_nikhahit",3664:"Thai_leksun",3665:"Thai_leknung",3666:"Thai_leksong",3667:"Thai_leksam",3668:"Thai_leksi",3669:"Thai_lekha",3670:"Thai_lekhok",3671:"Thai_lekchet",3672:"Thai_lekpaet",3673:"Thai_lekkao",12593:"Hangul_Kiyeog",12594:"Hangul_SsangKiyeog",12595:"Hangul_KiyeogSios",12596:"Hangul_Nieun",12597:"Hangul_NieunJieuj",12598:"Hangul_NieunHieuh",12599:"Hangul_Dikeud",12600:"Hangul_SsangDikeud",12601:"Hangul_Rieul",12602:"Hangul_RieulKiyeog",12603:"Hangul_RieulMieum",12604:"Hangul_RieulPieub",12605:"Hangul_RieulSios",12606:"Hangul_RieulTieut",12607:"Hangul_RieulPhieuf",12608:"Hangul_RieulHieuh",12609:"Hangul_Mieum",12610:"Hangul_Pieub",12611:"Hangul_SsangPieub",12612:"Hangul_PieubSios",12613:"Hangul_Sios",12614:"Hangul_SsangSios",12615:"Hangul_Ieung",12616:"Hangul_Jieuj",12617:"Hangul_SsangJieuj",12618:"Hangul_Cieuc",12619:"Hangul_Khieuq",12620:"Hangul_Tieut",12621:"Hangul_Phieuf",12622:"Hangul_Hieuh",12623:"Hangul_A",12624:"Hangul_AE",12625:"Hangul_YA",12626:"Hangul_YAE",12627:"Hangul_EO",12628:"Hangul_E",12629:"Hangul_YEO",12630:"Hangul_YE",12631:"Hangul_O",12632:"Hangul_WA",12633:"Hangul_WAE",12634:"Hangul_OE",12635:"Hangul_YO",12636:"Hangul_U",12637:"Hangul_WEO",12638:"Hangul_WE",12639:"Hangul_WI",12640:"Hangul_YU",12641:"Hangul_EU",12642:"Hangul_YI",12643:"Hangul_I",4520:"Hangul_J_Kiyeog",4521:"Hangul_J_SsangKiyeog",4522:"Hangul_J_KiyeogSios",4523:"Hangul_J_Nieun",4524:"Hangul_J_NieunJieuj",4525:"Hangul_J_NieunHieuh",4526:"Hangul_J_Dikeud",4527:"Hangul_J_Rieul",4528:"Hangul_J_RieulKiyeog",4529:"Hangul_J_RieulMieum",4530:"Hangul_J_RieulPieub",4531:"Hangul_J_RieulSios",4532:"Hangul_J_RieulTieut",4533:"Hangul_J_RieulPhieuf",4534:"Hangul_J_RieulHieuh",4535:"Hangul_J_Mieum",4536:"Hangul_J_Pieub",4537:"Hangul_J_PieubSios",4538:"Hangul_J_Sios",4539:"Hangul_J_SsangSios",4540:"Hangul_J_Ieung",4541:"Hangul_J_Jieuj",4542:"Hangul_J_Cieuc",4543:"Hangul_J_Khieuq",4544:"Hangul_J_Tieut",4545:"Hangul_J_Phieuf",4546:"Hangul_J_Hieuh",12653:"Hangul_RieulYeorinHieuh",12657:"Hangul_SunkyeongeumMieum",12664:"Hangul_SunkyeongeumPieub",12671:"Hangul_PanSios",12673:"Hangul_KkogjiDalrinIeung",12676:"Hangul_SunkyeongeumPhieuf",12678:"Hangul_YeorinHieuh",12685:"Hangul_AraeA",12686:"Hangul_AraeAE",4587:"Hangul_J_PanSios",4592:"Hangul_J_KkogjiDalrinIeung",4601:"Hangul_J_YeorinHieuh",338:"OE",339:"oe",376:"Ydiaeresis",8352:"EcuSign",8353:"ColonSign",8354:"CruzeiroSign",8355:"FFrancSign",8356:"LiraSign",8357:"MillSign",8358:"NairaSign",8359:"PesetaSign",8360:"RupeeSign",8361:"WonSign",8362:"NewSheqelSign",8363:"DongSign",8364:"EuroSign",768:"dead_grave",769:"dead_acute",770:"dead_circumflex",771:"dead_tilde",772:"dead_macron",774:"dead_breve",775:"dead_abovedot",776:"dead_diaeresis",778:"dead_abovering",779:"dead_doubleacute",780:"dead_caron",807:"dead_cedilla",808:"dead_ogonek",837:"dead_iota",12441:"dead_voiced_sound",12442:"dead_semivoiced_sound",8:"BackSpace",9:"Tab",10:"Linefeed",11:"Clear",13:"Return",19:"Pause",20:"Scroll_Lock",21:"Sys_Req",27:"Escape",1169:"Ukrainian_ghe_with_upturn",1168:"Ukrainian_GHE_WITH_UPTURN",1415:"Armenian_ligature_ew",1417:"Armenian_verjaket",1373:"Armenian_but",1418:"Armenian_yentamna",1372:"Armenian_amanak",1371:"Armenian_shesht",1374:"Armenian_paruyk",1329:"Armenian_AYB",1377:"Armenian_ayb",1330:"Armenian_BEN",1378:"Armenian_ben",1331:"Armenian_GIM",1379:"Armenian_gim",1332:"Armenian_DA",1380:"Armenian_da",1333:"Armenian_YECH",1381:"Armenian_yech",1334:"Armenian_ZA",1382:"Armenian_za",1335:"Armenian_E",1383:"Armenian_e",1336:"Armenian_AT",1384:"Armenian_at",1337:"Armenian_TO",1385:"Armenian_to",1338:"Armenian_ZHE",1386:"Armenian_zhe",1339:"Armenian_INI",1387:"Armenian_ini",1340:"Armenian_LYUN",1388:"Armenian_lyun",1341:"Armenian_KHE",1389:"Armenian_khe",1342:"Armenian_TSA",1390:"Armenian_tsa",1343:"Armenian_KEN",1391:"Armenian_ken",1344:"Armenian_HO",1392:"Armenian_ho",1345:"Armenian_DZA",1393:"Armenian_dza",1346:"Armenian_GHAT",1394:"Armenian_ghat",1347:"Armenian_TCHE",1395:"Armenian_tche",1348:"Armenian_MEN",1396:"Armenian_men",1349:"Armenian_HI",1397:"Armenian_hi",1350:"Armenian_NU",1398:"Armenian_nu",1351:"Armenian_SHA",1399:"Armenian_sha",1352:"Armenian_VO",1400:"Armenian_vo",1353:"Armenian_CHA",1401:"Armenian_cha",1354:"Armenian_PE",1402:"Armenian_pe",1355:"Armenian_JE",1403:"Armenian_je",1356:"Armenian_RA",1404:"Armenian_ra",1357:"Armenian_SE",1405:"Armenian_se",1358:"Armenian_VEV",1406:"Armenian_vev",1359:"Armenian_TYUN",1407:"Armenian_tyun",1360:"Armenian_RE",1408:"Armenian_re",1361:"Armenian_TSO",1409:"Armenian_tso",1362:"Armenian_VYUN",1410:"Armenian_vyun",1363:"Armenian_PYUR",1411:"Armenian_pyur",1364:"Armenian_KE",1412:"Armenian_ke",1365:"Armenian_O",1413:"Armenian_o",1366:"Armenian_FE",1414:"Armenian_fe",1370:"Armenian_apostrophe",4304:"Georgian_an",4305:"Georgian_ban",4306:"Georgian_gan",4307:"Georgian_don",4308:"Georgian_en",4309:"Georgian_vin",4310:"Georgian_zen",4311:"Georgian_tan",4312:"Georgian_in",4313:"Georgian_kan",4314:"Georgian_las",4315:"Georgian_man",4316:"Georgian_nar",4317:"Georgian_on",4318:"Georgian_par",4319:"Georgian_zhar",4320:"Georgian_rae",4321:"Georgian_san",4322:"Georgian_tar",4323:"Georgian_un",4324:"Georgian_phar",4325:"Georgian_khar",4326:"Georgian_ghan",4327:"Georgian_qar",4328:"Georgian_shin",4329:"Georgian_chin",4330:"Georgian_can",4331:"Georgian_jil",4332:"Georgian_cil",4333:"Georgian_char",4334:"Georgian_xan",4335:"Georgian_jhan",4336:"Georgian_hae",4337:"Georgian_he",4338:"Georgian_hie",4339:"Georgian_we",4340:"Georgian_har",4341:"Georgian_hoe",4342:"Georgian_fi",7682:"Babovedot",7683:"babovedot",7690:"Dabovedot",7808:"Wgrave",7810:"Wacute",7691:"dabovedot",7922:"Ygrave",7710:"Fabovedot",7711:"fabovedot",7744:"Mabovedot",7745:"mabovedot",7766:"Pabovedot",7809:"wgrave",7767:"pabovedot",7811:"wacute",7776:"Sabovedot",7923:"ygrave",7812:"Wdiaeresis",7813:"wdiaeresis",7777:"sabovedot",372:"Wcircumflex",7786:"Tabovedot",374:"Ycircumflex",373:"wcircumflex",7787:"tabovedot",375:"ycircumflex",1776:"Farsi_0",1777:"Farsi_1",1778:"Farsi_2",1779:"Farsi_3",1780:"Farsi_4",1781:"Farsi_5",1782:"Farsi_6",1783:"Farsi_7",1784:"Farsi_8",1785:"Farsi_9",1642:"Arabic_percent",1648:"Arabic_superscript_alef",1657:"Arabic_tteh",1662:"Arabic_peh",1670:"Arabic_tcheh",1672:"Arabic_ddal",1681:"Arabic_rreh",1748:"Arabic_fullstop",1632:"Arabic_0",1633:"Arabic_1",1634:"Arabic_2",1635:"Arabic_3",1636:"Arabic_4",1637:"Arabic_5",1638:"Arabic_6",1639:"Arabic_7",1640:"Arabic_8",1641:"Arabic_9",1619:"Arabic_madda_above",1620:"Arabic_hamza_above",1621:"Arabic_hamza_below",1688:"Arabic_jeh",1700:"Arabic_veh",1705:"Arabic_keheh",1711:"Arabic_gaf",1722:"Arabic_noon_ghunna",1726:"Arabic_heh_doachashmee",1740:"Farsi_yeh",1746:"Arabic_yeh_baree",1729:"Arabic_heh_goal",1170:"Cyrillic_GHE_bar",1174:"Cyrillic_ZHE_descender",1178:"Cyrillic_KA_descender",1180:"Cyrillic_KA_vertstroke",1186:"Cyrillic_EN_descender",1198:"Cyrillic_U_straight",1200:"Cyrillic_U_straight_bar",1202:"Cyrillic_HA_descender",1206:"Cyrillic_CHE_descender",1208:"Cyrillic_CHE_vertstroke",1210:"Cyrillic_SHHA",1240:"Cyrillic_SCHWA",1250:"Cyrillic_I_macron",1256:"Cyrillic_O_bar",1262:"Cyrillic_U_macron",1171:"Cyrillic_ghe_bar",1175:"Cyrillic_zhe_descender",1179:"Cyrillic_ka_descender",1181:"Cyrillic_ka_vertstroke",1187:"Cyrillic_en_descender",1199:"Cyrillic_u_straight",1201:"Cyrillic_u_straight_bar",1203:"Cyrillic_ha_descender",1207:"Cyrillic_che_descender",1209:"Cyrillic_che_vertstroke",1211:"Cyrillic_shha",1241:"Cyrillic_schwa",1251:"Cyrillic_i_macron",1257:"Cyrillic_o_bar",1263:"Cyrillic_u_macron",7818:"Xabovedot",300:"Ibreve",437:"Zstroke",486:"Gcaron",415:"Obarred",7819:"xabovedot",301:"ibreve",438:"zstroke",487:"gcaron",466:"ocaron",629:"obarred",399:"SCHWA",601:"schwa",7734:"Lbelowdot",7735:"lbelowdot",7840:"Abelowdot",7841:"abelowdot",7842:"Ahook",7843:"ahook",7844:"Acircumflexacute",7845:"acircumflexacute",7846:"Acircumflexgrave",7847:"acircumflexgrave",7848:"Acircumflexhook",7849:"acircumflexhook",7850:"Acircumflextilde",7851:"acircumflextilde",7852:"Acircumflexbelowdot",7853:"acircumflexbelowdot",7854:"Abreveacute",7855:"abreveacute",7856:"Abrevegrave",7857:"abrevegrave",7858:"Abrevehook",7859:"abrevehook",7860:"Abrevetilde",7861:"abrevetilde",7862:"Abrevebelowdot",7863:"abrevebelowdot",7864:"Ebelowdot",7865:"ebelowdot",7866:"Ehook",7867:"ehook",7868:"Etilde",7869:"etilde",7870:"Ecircumflexacute",7871:"ecircumflexacute",7872:"Ecircumflexgrave",7873:"ecircumflexgrave",7874:"Ecircumflexhook",7875:"ecircumflexhook",7876:"Ecircumflextilde",7877:"ecircumflextilde",7878:"Ecircumflexbelowdot",7879:"ecircumflexbelowdot",7880:"Ihook",7881:"ihook",7882:"Ibelowdot",7883:"ibelowdot",7884:"Obelowdot",7885:"obelowdot",7886:"Ohook",7887:"ohook",7888:"Ocircumflexacute",7889:"ocircumflexacute",7890:"Ocircumflexgrave",7891:"ocircumflexgrave",7892:"Ocircumflexhook",7893:"ocircumflexhook",7894:"Ocircumflextilde",7895:"ocircumflextilde",7896:"Ocircumflexbelowdot",7897:"ocircumflexbelowdot",7898:"Ohornacute",7899:"ohornacute",7900:"Ohorngrave",7901:"ohorngrave",7902:"Ohornhook",7903:"ohornhook",7904:"Ohorntilde",7905:"ohorntilde",7906:"Ohornbelowdot",7907:"ohornbelowdot",7908:"Ubelowdot",7909:"ubelowdot",7910:"Uhook",7911:"uhook",7912:"Uhornacute",7913:"uhornacute",7914:"Uhorngrave",7915:"uhorngrave",7916:"Uhornhook",7917:"uhornhook",7918:"Uhorntilde",7919:"uhorntilde",7920:"Uhornbelowdot",7921:"uhornbelowdot",7924:"Ybelowdot",7925:"ybelowdot",7926:"Yhook",7927:"yhook",7928:"Ytilde",7929:"ytilde",416:"Ohorn",417:"ohorn",431:"Uhorn",432:"uhorn",803:"dead_belowdot",777:"dead_hook",795:"dead_horn"}),pe=Object.freeze({AltLeft:"Alt_L",AltRight:"Alt_R",ArrowDown:"Down",ArrowLeft:"Left",ArrowRight:"Right",ArrowUp:"Up",Backspace:"BackSpace",CapsLock:"Caps_Lock",ControlLeft:"Control_L",ControlRight:"Control_R",Enter:"Return",HyperLeft:"Hyper_L",HyperRight:"Hyper_R",NumLock:"Num_Lock",NumpadEnter:"Return",MetaLeft:"Meta_L",MetaRight:"Meta_R",PageDown:"Page_Down",PageUp:"Page_Up",ScrollLock:"Scroll_Lock",ShiftLeft:"Shift_L",ShiftRight:"Shift_R",SuperLeft:"Super_L",SuperRight:"Super_R"}),ue=new Set(["Clear","Copy","Cut","Delete","End","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","Home","Insert","Paste","Redo","Tab","Undo"]);function me(e,t){var n="Unidentified";if(1===e.length){const t=e.charCodeAt(0);t in he&&(n=he[t])}else t in pe?n=pe[t]:ue.has(t)&&(n=t);return n}const _e=Object.freeze(["wheel","contextmenu","mousemove","mousedown","mouseup","touchstart","touchend","touchmove","touchcancel","keyup","keydown"]),fe=Object.freeze({mousemove:"MouseMove",mousedown:"MouseButtonPress",mouseup:"MouseButtonRelease"}),ge=Object.freeze({touchstart:"TouchDown",touchend:"TouchUp",touchmove:"TouchMotion",touchcancel:"TouchUp"}),Ce=Object.freeze({keydown:"KeyPress",keyup:"KeyRelease"});function ye(e){const t=[];return e.altKey&&t.push("mod1-mask"),e.ctrlKey&&t.push("control-mask"),e.metaKey&&t.push("meta-mask"),e.shiftKey&&t.push("shift-mask"),t.join("+")}class ve extends EventTarget{constructor(e,t){super(),this._rtcDataChannel=e,this._consumerSession=t,this._videoElement=null,this._videoElementComputedStyle=null,this._videoElementKeyboard=null,this._lastTouchEventTimestamp=0,this._requestCounter=0,e.addEventListener("close",(()=>{this._rtcDataChannel===e&&this.close()})),e.addEventListener("error",(t=>{if(this._rtcDataChannel===e){const e=t.error;this.dispatchEvent(new ErrorEvent("error",{message:e&&e.message||"Remote controller error",error:e||new Error("unknown error on the remote controller data channel")}))}})),e.addEventListener("message",(e=>{try{const t=JSON.parse(e.data);"ControlResponseMessage"===t.type?this.dispatchEvent(new CustomEvent("controlResponse",{detail:t})):"InfoMessage"===t.type&&this.dispatchEvent(new CustomEvent("info",{detail:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse control message from signaling server",error:e}))}}))}get rtcDataChannel(){return this._rtcDataChannel}get consumerSession(){return this._consumerSession}get videoElement(){return this._videoElement}attachVideoElement(e){if(e instanceof HTMLVideoElement&&e!==this._videoElement){this._videoElement&&this.attachVideoElement(null),this._videoElement=e,this._videoElementComputedStyle=window.getComputedStyle(e);for(const t of _e)e.addEventListener(t,this);e.setAttribute("tabindex","0")}else if(null===e&&this._videoElement){const e=this._videoElement;e.removeAttribute("tabindex"),this._videoElement=null,this._videoElementComputedStyle=null,this._lastTouchEventTimestamp=0;for(const t of _e)e.removeEventListener(t,this)}}sendControlRequest(e){try{if(!e||"object"!=typeof e&&"string"!=typeof e)throw new Error("invalid request");if(!this._rtcDataChannel)throw new Error("remote controller data channel is closed");let t={id:this._requestCounter++,request:e};return this._rtcDataChannel.send(JSON.stringify(t)),t.id}catch(e){return this.dispatchEvent(new ErrorEvent("error",{message:`cannot send control message over session ${this._consumerSession.sessionId} remote controller`,error:e})),-1}}close(){this.attachVideoElement(null);const e=this._rtcDataChannel;this._rtcDataChannel=null,e&&(e.close(),this.dispatchEvent(new Event("closed")))}_sendGstNavigationEvent(e){let t={type:"navigationEvent",event:e};this.sendControlRequest(t)}_computeVideoMousePosition(e){const t={x:0,y:0};if(!this._videoElement||this._videoElement.videoWidth<=0||this._videoElement.videoHeight<=0)return t;const n=parseFloat(this._videoElementComputedStyle.paddingLeft),r=parseFloat(this._videoElementComputedStyle.paddingRight),i=parseFloat(this._videoElementComputedStyle.paddingTop),o=parseFloat(this._videoElementComputedStyle.paddingBottom);if("offsetX"in e&&"offsetY"in e)t.x=e.offsetX-n,t.y=e.offsetY-i;else{const r=this._videoElement.getBoundingClientRect(),o={left:parseFloat(this._videoElementComputedStyle.borderLeftWidth),top:parseFloat(this._videoElementComputedStyle.borderTopWidth)};t.x=e.clientX-r.left-o.left-n,t.y=e.clientY-r.top-o.top-i}const s={x:this._videoElement.clientWidth-(n+r),y:this._videoElement.clientHeight-(i+o)},a=Math.min(s.x/this._videoElement.videoWidth,s.y/this._videoElement.videoHeight);s.x=Math.max(.5*(s.x-this._videoElement.videoWidth*a),0),s.y=Math.max(.5*(s.y-this._videoElement.videoHeight*a),0);const c=0!==a?1/a:0;return t.x=(t.x-s.x)*c,t.y=(t.y-s.y)*c,t.x=Math.min(Math.max(t.x,0),this._videoElement.videoWidth),t.y=Math.min(Math.max(t.y,0),this._videoElement.videoHeight),t}handleEvent(e){if(this._videoElement)switch(e.type){case"wheel":e.preventDefault();{const t=this._computeVideoMousePosition(e);this._sendGstNavigationEvent({event:"MouseScroll",x:t.x,y:t.y,delta_x:-e.deltaX,delta_y:-e.deltaY,modifier_state:ye(e)})}break;case"contextmenu":e.preventDefault();break;case"mousemove":case"mousedown":case"mouseup":e.preventDefault();{const t=this._computeVideoMousePosition(e),n={event:fe[e.type],x:t.x,y:t.y,modifier_state:ye(e)};"mousemove"!==e.type&&(n.button=e.button+1,"mousedown"===e.type&&0===e.button&&this._videoElement.focus()),this._sendGstNavigationEvent(n)}break;case"touchstart":case"touchend":case"touchmove":case"touchcancel":for(const t of e.changedTouches){const n=this._computeVideoMousePosition(t),r={event:ge[e.type],identifier:t.identifier,x:n.x,y:n.y,modifier_state:ye(e)};!("force"in t)||"touchstart"!==e.type&&"touchmove"!==e.type||(r.pressure=t.force),this._sendGstNavigationEvent(r)}e.timeStamp>this._lastTouchEventTimestamp&&(this._lastTouchEventTimestamp=e.timeStamp,this._sendGstNavigationEvent({event:"TouchFrame",modifier_state:ye(e)}));break;case"keyup":case"keydown":e.preventDefault();{const t={event:Ce[e.type],key:me(e.key,e.code),modifier_state:ye(e)};this._sendGstNavigationEvent(t)}}}}const be=ve;const Se=class extends le{constructor(e,t,n){super(e,t),this._streams=[],this._remoteController=null,this._pendingCandidates=[],this._mungeStereoHack=!1,this._offerOptions=n,this.addEventListener("closed",(()=>{this._streams=[],this._pendingCandidates=[],this._remoteController&&this._remoteController.close()}))}set mungeStereoHack(e){"boolean"==typeof e&&(this._mungeStereoHack=e)}get streams(){return this._streams}get remoteController(){return this._remoteController}connect(){if(!this._comChannel||this._state===ce.closed)return!1;if(this._state!==ce.idle)return!0;if(this._offerOptions)this.ensurePeerConnection(),this._rtcPeerConnection.createDataChannel("control"),this._rtcPeerConnection.createOffer(this._offerOptions).then((e=>{if(this._rtcPeerConnection&&e)return this._rtcPeerConnection.setLocalDescription(e);throw new Error("cannot send local offer to WebRTC peer")})).then((()=>{if(this._rtcPeerConnection&&this._comChannel){const e={type:"startSession",peerId:this._peerId,offer:this._rtcPeerConnection.localDescription.toJSON().sdp};if(!this._comChannel.send(e))throw new Error("cannot send startSession message to signaling server");this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged"))}})).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}));else{const e={type:"startSession",peerId:this._peerId};if(!this._comChannel.send(e))return this.dispatchEvent(new ErrorEvent("error",{message:"cannot connect consumer session",error:new Error("cannot send startSession message to signaling server")})),this.close(),!1;this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged"))}return!0}onSessionStarted(e,t){if(this._peerId===e&&this._state===ce.connecting&&!this._sessionId){console.log("Session started",this._sessionId),this._sessionId=t;for(const e of this._pendingCandidates)console.log("Sending delayed ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.toJSON()});this._pendingCandidates=[]}}ensurePeerConnection(){if(!this._rtcPeerConnection){const e=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=e,e.ontrack=t=>{if(this._rtcPeerConnection===e&&t.streams&&t.streams.length>0){this._state===ce.connecting&&(this._state=ce.streaming,this.dispatchEvent(new Event("stateChanged")));let e=!1;for(const n of t.streams)this._streams.includes(n)||(this._streams.push(n),e=!0);e&&this.dispatchEvent(new Event("streamsChanged"))}},e.ondatachannel=e=>{const t=e.channel;if(t&&"control"===t.label){if(this._remoteController){const e=this._remoteController;this._remoteController=null,e.close()}const e=new be(t,this);this._remoteController=e,this.dispatchEvent(new Event("remoteControllerChanged")),e.addEventListener("closed",(()=>{this._remoteController===e&&(this._remoteController=null,this.dispatchEvent(new Event("remoteControllerChanged")))}))}},e.onicecandidate=t=>{this._rtcPeerConnection===e&&t.candidate&&this._comChannel&&(this._sessionId?(console.log("Sending ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:t.candidate.toJSON()})):this._pendingCandidates.push(t.candidate))},this.dispatchEvent(new Event("rtcPeerConnectionChanged"))}}mungeStereo(e,t){const n=/a=fmtp:.* sprop-stereo/g;let r=new Set;for(const t of e.matchAll(n)){const e=t[0].match(/a=fmtp:(\d+) .*/);e&&r.add(e[1])}for(const e of r){const n=new RegExp("a=fmtp:"+e+".*stereo");t.match(n)||(t=t.replaceAll("a=fmtp:"+e,"a=fmtp:"+e+" stereo=1;"))}return t}onSessionPeerMessage(e){if(this._state!==ce.closed&&this._comChannel&&this._sessionId)if(this.ensurePeerConnection(),e.sdp)this._offerOptions?this._rtcPeerConnection.setRemoteDescription(e.sdp).then((()=>{console.log("done")})).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())})):this._rtcPeerConnection.setRemoteDescription(e.sdp).then((()=>this._rtcPeerConnection?this._rtcPeerConnection.createAnswer():null)).then((t=>this._rtcPeerConnection&&t?(this._mungeStereoHack&&(t.sdp=this.mungeStereo(e.sdp.sdp,t.sdp)),this._rtcPeerConnection.setLocalDescription(t)):null)).then((()=>{if(this._rtcPeerConnection&&this._comChannel){console.log("Sending SDP with session id",this._sessionId);const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}})).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}));else{if(!e.ice)throw new Error(`invalid empty peer message received from consumer session ${this._sessionId}`);{const t=new RTCIceCandidate(e.ice);this._rtcPeerConnection.addIceCandidate(t).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())}))}}}};class ke extends le{constructor(e,t,n,r){super(e,n),this._sessionId=t,this._state=ce.streaming;const i=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=i;for(const e of r.getTracks())i.addTrack(e,r);i.onicecandidate=e=>{this._rtcPeerConnection===i&&e.candidate&&this._comChannel&&this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.candidate.toJSON()})},this.dispatchEvent(new Event("rtcPeerConnectionChanged")),i.setLocalDescription().then((()=>{if(this._rtcPeerConnection===i&&this._comChannel){const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}})).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}))}onSessionPeerMessage(e){if(this._state!==ce.closed&&this._rtcPeerConnection)if(e.sdp)this._rtcPeerConnection.setRemoteDescription(e.sdp).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}));else{if(!e.ice)throw new Error(`invalid empty peer message received from producer's client session ${this._peerId}`);{const t=new RTCIceCandidate(e.ice);this._rtcPeerConnection.addIceCandidate(t).catch((e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())}))}}}}class Te extends EventTarget{constructor(e,t){super(),this._comChannel=e,this._stream=t,this._state=ce.idle,this._clientSessions={}}get stream(){return this._stream}get state(){return this._state}start(){if(!this._comChannel||this._state===ce.closed)return!1;if(this._state!==ce.idle)return!0;const e={type:"setPeerStatus",roles:["listener","producer"],meta:this._comChannel.meta};return this._comChannel.send(e)?(this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged")),!0):(this.dispatchEvent(new ErrorEvent("error",{message:"cannot start producer session",error:new Error("cannot register producer to signaling server")})),this.close(),!1)}close(){if(this._state!==ce.closed){for(const e of this._stream.getTracks())e.stop();this._state!==ce.idle&&this._comChannel&&this._comChannel.send({type:"setPeerStatus",roles:["listener"],meta:this._comChannel.meta}),this._state=ce.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._stream=null;for(const e of Object.values(this._clientSessions))e.close();this._clientSessions={},this.dispatchEvent(new Event("closed"))}}onProducerRegistered(){this._state===ce.connecting&&(this._state=ce.streaming,this.dispatchEvent(new Event("stateChanged")))}onStartSessionMessage(e){if(this._comChannel&&this._stream&&!(e.sessionId in this._clientSessions)){const t=new ke(e.peerId,e.sessionId,this._comChannel,this._stream);this._clientSessions[e.sessionId]=t,t.addEventListener("closed",(e=>{const n=e.target.sessionId;n in this._clientSessions&&this._clientSessions[n]===t&&(delete this._clientSessions[n],this.dispatchEvent(new CustomEvent("clientConsumerRemoved",{detail:t})))})),t.addEventListener("error",(e=>{this.dispatchEvent(new ErrorEvent("error",{message:`error from client consumer ${e.target.peerId}: ${e.message}`,error:e.error}))})),this.dispatchEvent(new CustomEvent("clientConsumerAdded",{detail:t}))}}onEndSessionMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].close()}onSessionPeerMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].onSessionPeerMessage(e)}}const Ee=Te,Pe=Object.freeze({welcome:"welcome",peerStatusChanged:"peerStatusChanged",list:"list",sessionStarted:"sessionStarted",peer:"peer",startSession:"startSession",endSession:"endSession",error:"error"});function Re(e,t){if(!e||"object"!=typeof e)return null;const n={id:"",meta:{}};if(e.id&&"string"==typeof e.id)n.id=e.id;else{if(!e.peerId||"string"!=typeof e.peerId)return null;n.id=e.peerId}return n.id===t?null:(e.meta&&"object"==typeof e.meta&&(n.meta=e.meta),Object.freeze(n.meta),Object.freeze(n))}class we extends EventTarget{constructor(e,t,n){super(),this._meta=t,this._webrtcConfig=n,this._ws=new WebSocket(e),this._ready=!1,this._channelId="",this._producerSession=null,this._consumerSessions={},this._ws.onerror=e=>{this.dispatchEvent(new ErrorEvent("error",{message:e.message||"WebSocket error",error:e.error||new Error(this._ready?"transportation error":"cannot connect to signaling server")})),this.close()},this._ws.onclose=()=>{this._ready=!1,this._channelId="",this._ws=null,this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null),this.dispatchEvent(new Event("closed"))},this._ws.onmessage=e=>{try{const n=JSON.parse(e.data);if(n&&"object"==typeof n)switch(n.type){case Pe.welcome:this._channelId=n.peerId;try{this._ws.send(JSON.stringify({type:"setPeerStatus",roles:["listener"],meta:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot initialize connection to signaling server",error:e})),this.close()}break;case Pe.peerStatusChanged:if(n.peerId===this._channelId)!this._ready&&n.roles.includes("listener")&&(this._ready=!0,this.dispatchEvent(new Event("ready")),this.send({type:"list"})),this._producerSession&&n.roles.includes("producer")&&this._producerSession.onProducerRegistered();else{const e=Re(n,this._channelId);e&&(n.roles.includes("producer")?this.dispatchEvent(new CustomEvent("producerAdded",{detail:e})):this.dispatchEvent(new CustomEvent("producerRemoved",{detail:e})))}break;case Pe.list:for(const e of n.producers){const t=Re(e,this._channelId);t&&this.dispatchEvent(new CustomEvent("producerAdded",{detail:t}))}break;case Pe.sessionStarted:{const e=this.getConsumerSession(n.peerId);e&&(delete this._consumerSessions[n.peerId],e.onSessionStarted(n.peerId,n.sessionId),e.sessionId&&!(e.sessionId in this._consumerSessions)?this._consumerSessions[e.sessionId]=e:e.close())}break;case Pe.peer:{const e=this.getConsumerSession(n.sessionId);e?e.onSessionPeerMessage(n):this._producerSession&&this._producerSession.onSessionPeerMessage(n)}break;case Pe.startSession:this._producerSession&&this._producerSession.onStartSessionMessage(n);break;case Pe.endSession:{const e=this.getConsumerSession(n.sessionId);e?e.close():this._producerSession&&this._producerSession.onEndSessionMessage(n)}break;case Pe.error:this.dispatchEvent(new ErrorEvent("error",{message:"error received from signaling server",error:new Error(n.details)}));break;default:throw new Error(`unknown message type: "${n.type}"`)}}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse incoming message from signaling server",error:e}))}}}get meta(){return this._meta}get webrtcConfig(){return this._webrtcConfig}get ready(){return this._ready}get channelId(){return this._channelId}get producerSession(){return this._producerSession}createProducerSession(e){if(!(this._ready&&e instanceof MediaStream))return null;if(this._producerSession)return this._producerSession.stream===e?this._producerSession:null;const t=new Ee(this,e);return this._producerSession=t,t.addEventListener("closed",(()=>{this._producerSession===t&&(this._producerSession=null)})),t}createConsumerSession(e,t){if(!this._ready||!e||"string"!=typeof e)return null;if(t&&"object"!=typeof t&&(t=void 0),e in this._consumerSessions)return this._consumerSessions[e];for(const t of Object.values(this._consumerSessions))if(t.peerId===e)return t;const n=new Se(e,this,t);return this._consumerSessions[e]=n,n.addEventListener("closed",(e=>{let t=e.target.sessionId;t||(t=e.target.peerId),t in this._consumerSessions&&this._consumerSessions[t]===n&&delete this._consumerSessions[t]})),n}getConsumerSession(e){return e in this._consumerSessions?this._consumerSessions[e]:null}closeAllConsumerSessions(){for(const e of Object.values(this._consumerSessions))e.close();this._consumerSessions={}}send(e){if(this._ready&&e&&"object"==typeof e)try{return this._ws.send(JSON.stringify(e)),!0}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot send message to signaling server",error:e}))}return!1}close(){this._ws&&(this._ready=!1,this._channelId="",this._ws.close(),this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null))}}const Ae=we;class xe{constructor(e){this._channel=null,this._producers={},this._connectionListeners=[],this._producersListeners=[];const t=Object.assign({},se);e&&"object"==typeof e&&Object.assign(t,e),"object"!=typeof t.meta&&(t.meta=null),this._config=t,this.connectChannel()}registerConnectionListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.connected||"function"!=typeof e.disconnected)&&(this._connectionListeners.includes(e)||this._connectionListeners.push(e),!0)}unregisterConnectionListener(e){const t=this._connectionListeners.indexOf(e);return t>=0&&(this._connectionListeners.splice(t,1),!0)}unregisterAllConnectionListeners(){this._connectionListeners=[]}createProducerSession(e){return this._channel?this._channel.createProducerSession(e):null}getAvailableProducers(){return Object.values(this._producers)}registerProducersListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.producerAdded||"function"!=typeof e.producerRemoved)&&(this._producersListeners.includes(e)||this._producersListeners.push(e),!0)}unregisterProducersListener(e){const t=this._producersListeners.indexOf(e);return t>=0&&(this._producersListeners.splice(t,1),!0)}unregisterAllProducersListeners(){this._producersListeners=[]}createConsumerSession(e){return this._channel?this._channel.createConsumerSession(e):null}createConsumerSessionWithOfferOptions(e,t){return this._channel?this._channel.createConsumerSession(e,t):null}connectChannel(){if(this._channel){const e=this._channel;this._channel=null,e.close();for(const e in this._producers)this.triggerProducerRemoved(e);this._producers={},this.triggerDisconnected()}this._channel=new Ae(this._config.signalingServerUrl,this._config.meta,this._config.webrtcConfig),this._channel.addEventListener("error",(e=>{e.target===this._channel&&console.error(e.message,e.error)})),this._channel.addEventListener("closed",(e=>{if(e.target===this._channel){this._channel=null;for(const e in this._producers)this.triggerProducerRemoved(e);this._producers={},this.triggerDisconnected(),this._config.reconnectionTimeout>0&&window.setTimeout((()=>{this.connectChannel()}),this._config.reconnectionTimeout)}})),this._channel.addEventListener("ready",(e=>{e.target===this._channel&&this.triggerConnected(this._channel.channelId)})),this._channel.addEventListener("producerAdded",(e=>{e.target===this._channel&&this.triggerProducerAdded(e.detail)})),this._channel.addEventListener("producerRemoved",(e=>{e.target===this._channel&&this.triggerProducerRemoved(e.detail.id)}))}triggerConnected(e){for(const t of this._connectionListeners)try{t.connected(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerDisconnected(){for(const e of this._connectionListeners)try{e.disconnected()}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerProducerAdded(e){if(!(e.id in this._producers)){this._producers[e.id]=e;for(const t of this._producersListeners)try{t.producerAdded(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}}triggerProducerRemoved(e){if(e in this._producers){const t=this._producers[e];delete this._producers[e];for(const e of this._producersListeners)try{e.producerRemoved(t)}catch(e){console.error("a listener callback should not throw any exception",e)}}}}xe.SessionState=ce;const Ie=xe;window.GstWebRTCAPI||(window.GstWebRTCAPI=Ie)})()})();
+//# sourceMappingURL=gstwebrtc-api-2.0.0.min.js.map
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/apps.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/apps.js
new file mode 100644
index 0000000000000000000000000000000000000000..cd60df2852df2fca161c2c7a047e130df7810059
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/apps.js
@@ -0,0 +1,319 @@
+const installedApps = {
+ refreshAppList: async () => {
+ const appsData = await installedApps.fetchInstalledApps();
+ await installedApps.displayInstalledApps(appsData);
+ },
+
+ currentlyRunningApp: null,
+ busy: false,
+ toggles: {},
+
+ startApp: async (appName) => {
+ if (installedApps.busy) {
+ console.log(`Another app is currently being started or stopped.`);
+ return;
+ }
+ installedApps.setBusy(true);
+
+ console.log(`Current running app: ${installedApps.currentlyRunningApp}`);
+
+ if (installedApps.currentlyRunningApp) {
+ console.log(`Stopping currently running app: ${installedApps.currentlyRunningApp}...`);
+ await installedApps.stopApp(installedApps.currentlyRunningApp, true);
+ }
+
+ console.log(`Starting app: ${appName}...`);
+ const endpoint = `/api/apps/start-app/${appName}`;
+ const resp = await fetch(endpoint, { method: 'POST' });
+ if (!resp.ok) {
+ console.error(`Failed to staret app ${appName}: ${resp.statusText}`);
+ installedApps.toggles[appName].setChecked(false);
+ installedApps.setBusy(false);
+ return;
+ } else {
+ console.log(`App ${appName} started successfully.`);
+ }
+
+ installedApps.currentlyRunningApp = appName;
+ installedApps.setBusy(false);
+ },
+
+ stopApp: async (appName, force = false) => {
+ if (installedApps.busy && !force) {
+ console.log(`Another app is currently being started or stopped.`);
+ return;
+ }
+ installedApps.setBusy(true);
+
+ console.log(`Stopping app: ${appName}...`);
+
+ if (force) {
+ console.log(`Force stopping app: ${appName}...`);
+ installedApps.toggles[appName].setChecked(false);
+ }
+
+ const endpoint = `/api/apps/stop-current-app`;
+ const resp = await fetch(endpoint, { method: 'POST' });
+ if (!resp.ok) {
+ console.error(`Failed to stop app ${appName}: ${resp.statusText}`);
+ installedApps.setBusy(false);
+ return;
+ } else {
+ console.log(`App ${appName} stopped successfully.`);
+ installedApps.toggles[appName].setChecked(false);
+ }
+
+ if (installedApps.currentlyRunningApp === appName) {
+ installedApps.currentlyRunningApp = null;
+ }
+ installedApps.setBusy(false);
+ },
+
+ setBusy: (isBusy) => {
+ installedApps.busy = isBusy;
+ for (const toggle of Object.values(installedApps.toggles)) {
+ if (isBusy) {
+ toggle.disable();
+ } else {
+ toggle.enable();
+ }
+ }
+ },
+
+ fetchInstalledApps: async () => {
+ const resp = await fetch('/api/apps/list-available/installed');
+ const appsData = await resp.json();
+ return appsData;
+ },
+
+ displayInstalledApps: async (appsData) => {
+ const appsListElement = document.getElementById('installed-apps');
+ appsListElement.innerHTML = '';
+
+ if (!appsData || appsData.length === 0) {
+ appsListElement.innerHTML = 'No installed apps found. ';
+ return;
+ }
+
+ const runningApp = await installedApps.getRunningApp();
+
+ installedApps.toggles = {};
+ appsData.forEach(app => {
+ const li = document.createElement('li');
+ li.className = 'app-list-item';
+ const isRunning = (app.name === runningApp);
+ li.appendChild(installedApps.createAppElement(app, isRunning));
+ appsListElement.appendChild(li);
+ });
+ },
+
+ createAppElement: (app, isRunning) => {
+ const container = document.createElement('div');
+ container.className = 'grid grid-cols-[auto_6rem_2rem] justify-stretch gap-x-2';
+
+ const title = document.createElement('div');
+ const titleSpan = document.createElement('span');
+ titleSpan.className = 'installed-app-title top-1/2 ';
+ titleSpan.innerHTML = app.name;
+ title.appendChild(titleSpan);
+ if (app.extra && app.extra.custom_app_url) {
+ const settingsLink = document.createElement('a');
+ settingsLink.className = 'installed-app-settings ml-2 text-gray-500 cursor-pointer';
+ settingsLink.innerHTML = '⚙️';
+ settingsLink.href = app.extra.custom_app_url;
+ settingsLink.target = '_blank';
+ settingsLink.rel = 'noopener noreferrer';
+ title.appendChild(settingsLink);
+ }
+ container.appendChild(title);
+ const slider = document.createElement('div');
+ const toggle = new ToggleSlider({
+ checked: isRunning,
+ onChange: (checked) => {
+ if (installedApps.busy) {
+ toggle.setChecked(!checked);
+ return;
+ }
+ if (checked) {
+ installedApps.startApp(app.name);
+ } else {
+ installedApps.stopApp(app.name);
+ }
+ }
+ });
+ installedApps.toggles[app.name] = toggle;
+ slider.appendChild(toggle.element);
+ container.appendChild(slider);
+
+ const remove = document.createElement('button');
+ remove.innerHTML = '🗑️';
+ remove.className = '-translate-y-1 text-xl';
+ container.appendChild(remove);
+ remove.onclick = async () => {
+ console.log(`Removing ${app.name}...`);
+ const resp = await fetch(`/api/apps/remove/${app.name}`, { method: 'POST' });
+ const data = await resp.json();
+ const jobId = data.job_id;
+
+ installedApps.appUninstallLogHandler(app.name, jobId);
+ };
+
+ return container;
+ },
+
+ getRunningApp: async () => {
+ const resp = await fetch('/api/apps/current-app-status');
+ const data = await resp.json();
+ if (!data) {
+ return null;
+ }
+ installedApps.currentlyRunningApp = data.info.name;
+ return data.info.name;
+ },
+
+ appUninstallLogHandler: async (appName, jobId) => {
+ const installModal = document.getElementById('install-modal');
+ const modalTitle = installModal.querySelector('#modal-title');
+ modalTitle.textContent = `Uninstalling ${appName}...`;
+ installModal.classList.remove('hidden');
+
+ const logsDiv = document.getElementById('install-logs');
+ logsDiv.textContent = '';
+
+ const closeButton = document.getElementById('modal-close-button');
+ closeButton.onclick = () => {
+ installModal.classList.add('hidden');
+ };
+ closeButton.classList = "hidden";
+ closeButton.textContent = '';
+
+ const ws = new WebSocket(`ws://${location.host}/api/apps/ws/apps-manager/${jobId}`);
+ ws.onmessage = (event) => {
+ try {
+ if (event.data.startsWith('{') && event.data.endsWith('}')) {
+
+ const data = JSON.parse(event.data);
+
+ if (data.status === "failed") {
+ closeButton.classList = "text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800";
+ closeButton.textContent = 'Close';
+ console.error(`Uninstallation of ${appName} failed.`);
+ } else if (data.status === "done") {
+ closeButton.classList = "text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800";
+ closeButton.textContent = 'Uninstall done';
+ console.log(`Uninstallation of ${appName} completed.`);
+
+ }
+ } else {
+ logsDiv.innerHTML += event.data + '\n';
+ logsDiv.scrollTop = logsDiv.scrollHeight;
+ }
+ } catch {
+ logsDiv.innerHTML += event.data + '\n';
+ logsDiv.scrollTop = logsDiv.scrollHeight;
+ }
+ };
+ ws.onclose = async () => {
+ hfAppsStore.refreshAppList();
+ installedApps.refreshAppList();
+ };
+ },
+};
+
+class ToggleSlider {
+ constructor({ checked = false, onChange = null } = {}) {
+ this.label = document.createElement('label');
+ this.label.className = 'relative inline-block w-28 h-8 cursor-pointer';
+
+ this.input = document.createElement('input');
+ this.input.type = 'checkbox';
+ this.input.className = 'sr-only peer';
+ this.input.checked = checked;
+ this.label.appendChild(this.input);
+
+ // Off label
+ this.offLabel = document.createElement('span');
+ this.offLabel.textContent = 'Off';
+ this.offLabel.className = 'absolute left-0 top-1/2 -translate-x-8 -translate-y-1/2 text-base select-none transition-colors duration-200 text-gray-900 peer-checked:text-gray-400';
+ this.label.appendChild(this.offLabel);
+
+ this.track = document.createElement('div');
+ this.track.className = 'absolute top-0 left-0 w-16 h-8 bg-gray-200 rounded-full transition-colors duration-200 peer-checked:bg-blue-800 dark:bg-gray-400 dark:peer-checked:bg-blue-800';
+ this.label.appendChild(this.track);
+
+ this.thumb = document.createElement('div');
+ this.thumb.className = 'absolute top-0.5 left-0.5 w-7 h-7 bg-white border border-gray-300 rounded-full transition-all duration-200';
+ this.track.appendChild(this.thumb);
+
+ // On label
+ this.onLabel = document.createElement('span');
+ this.onLabel.textContent = 'On';
+ this.onLabel.className = 'absolute right-0 top-1/2 -translate-y-1/2 -translate-x-4 text-base select-none transition-colors duration-200 text-gray-400 peer-checked:text-gray-900';
+ this.label.appendChild(this.onLabel);
+
+
+ this.input.addEventListener('change', () => {
+ if (this.input.checked) {
+ this.thumb.style.transform = 'translateX(31px)';
+ this.onLabel.classList.remove('text-gray-400');
+ this.onLabel.classList.add('text-gray-900');
+ this.offLabel.classList.remove('text-gray-900');
+ this.offLabel.classList.add('text-gray-400');
+ } else {
+ this.thumb.style.transform = 'translateX(0)';
+ this.onLabel.classList.remove('text-gray-900');
+ this.onLabel.classList.add('text-gray-400');
+ this.offLabel.classList.remove('text-gray-400');
+ this.offLabel.classList.add('text-gray-900');
+ }
+ if (onChange) onChange(this.input.checked);
+ });
+
+ // Set initial thumb and label color
+ if (checked) {
+ this.thumb.style.transform = 'translateX(31px)';
+ this.onLabel.classList.remove('text-gray-400');
+ this.onLabel.classList.add('text-gray-900');
+ } else {
+ this.onLabel.classList.remove('text-gray-900');
+ this.onLabel.classList.add('text-gray-400');
+ }
+
+ this.element = this.label;
+ }
+
+ setChecked(val) {
+ this.input.checked = val;
+ if (this.input.checked) {
+ this.thumb.style.transform = 'translateX(48px)';
+ this.onLabel.classList.remove('text-gray-400');
+ this.onLabel.classList.add('text-gray-900');
+ this.offLabel.classList.remove('text-gray-900');
+ this.offLabel.classList.add('text-gray-400');
+ } else {
+ this.thumb.style.transform = 'translateX(0)';
+ this.onLabel.classList.remove('text-gray-900');
+ this.onLabel.classList.add('text-gray-400');
+ this.offLabel.classList.remove('text-gray-400');
+ this.offLabel.classList.add('text-gray-900');
+ }
+ }
+
+ getChecked() {
+ return this.input.checked;
+ }
+
+ disable() {
+ this.input.disabled = true;
+ this.label.classList.add('opacity-50', 'pointer-events-none');
+ }
+
+ enable() {
+ this.input.disabled = false;
+ this.label.classList.remove('opacity-50', 'pointer-events-none');
+ }
+};
+
+window.addEventListener('load', async () => {
+ await installedApps.refreshAppList();
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/appstore.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/appstore.js
new file mode 100644
index 0000000000000000000000000000000000000000..e570fd73f7f8bb6962fcae989983eadf0225bad3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/appstore.js
@@ -0,0 +1,157 @@
+
+const hfAppsStore = {
+ refreshAppList: async () => {
+ const appsData = await hfAppsStore.fetchAvailableApps();
+ await hfAppsStore.displayAvailableApps(appsData);
+ },
+ fetchAvailableApps: async () => {
+ const resAvailable = await fetch('/api/apps/list-available/dashboard_selection');
+ const appsData = await resAvailable.json();
+ return appsData;
+ },
+
+ isInstalling: false,
+
+ installApp: async (app) => {
+ if (hfAppsStore.isInstalling) {
+ console.warn('An installation is already in progress.');
+ return;
+ }
+ hfAppsStore.isInstalling = true;
+
+ const appName = app.extra.cardData.title || app.name;
+ console.log(`Installing ${app.name}...`);
+
+ const resp = await fetch('/api/apps/install', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(app)
+ });
+ const data = await resp.json();
+ const jobId = data.job_id;
+
+ hfAppsStore.appInstallLogHandler(appName, jobId);
+ },
+
+ displayAvailableApps: async (appsData) => {
+ const appsListElement = document.getElementById('hf-available-apps');
+ appsListElement.innerHTML = '';
+
+ if (!appsData || appsData.length === 0) {
+ appsListElement.innerHTML = 'No available apps found. ';
+ return;
+ }
+
+ const hfApps = appsData.filter(app => app.source_kind === 'hf_space');
+ const installedApps = await fetch('/api/apps/list-available/installed').then(res => res.json());
+
+ hfApps.forEach(app => {
+ const li = document.createElement('li');
+ li.className = 'app-list-item';
+ const isInstalled = installedApps.some(installedApp => installedApp.name === app.name);
+ li.appendChild(hfAppsStore.createAppElement(app, isInstalled));
+ appsListElement.appendChild(li);
+ });
+ },
+
+ createAppElement: (app, isInstalled) => {
+ const container = document.createElement('div');
+ container.className = 'grid grid-cols-[2rem_auto_8rem] justify-stretch gap-x-6';
+
+ const iconDiv = document.createElement('div');
+ iconDiv.className = 'hf-app-icon row-span-2 my-1';
+ iconDiv.textContent = app.extra.cardData.emoji || '📦';
+ container.appendChild(iconDiv);
+
+ const nameDiv = document.createElement('div');
+ nameDiv.className = 'flex flex-col';
+
+ const titleDiv = document.createElement('a');
+ titleDiv.href = app.url;
+ titleDiv.target = '_blank';
+ titleDiv.rel = 'noopener noreferrer';
+ titleDiv.className = 'hf-app-title';
+ titleDiv.textContent = app.extra.cardData.title || app.name;
+ nameDiv.appendChild(titleDiv);
+ const descriptionDiv = document.createElement('span');
+ descriptionDiv.className = 'hf-app-description';
+ descriptionDiv.textContent = app.description || 'No description available.';
+ nameDiv.appendChild(descriptionDiv);
+ container.appendChild(nameDiv);
+
+ const installButtonDiv = document.createElement('button');
+ installButtonDiv.className = 'row-span-2 my-2 hf-app-install-button';
+
+ if (isInstalled) {
+ installButtonDiv.classList.add('bg-gray-400', 'cursor-not-allowed');
+ installButtonDiv.textContent = 'Installed';
+ installButtonDiv.disabled = true;
+ } else {
+ installButtonDiv.classList.add('border', 'border-red-600');
+ installButtonDiv.textContent = 'Install';
+ installButtonDiv.onclick = async () => {
+ hfAppsStore.installApp(app);
+ };
+ }
+
+ container.appendChild(installButtonDiv);
+
+ return container;
+ },
+
+ appInstallLogHandler: async (appName, jobId) => {
+ const installModal = document.getElementById('install-modal');
+ const modalTitle = installModal.querySelector('#modal-title');
+ modalTitle.textContent = `Installing ${appName}...`;
+ installModal.classList.remove('hidden');
+
+ const logsDiv = document.getElementById('install-logs');
+ logsDiv.textContent = '';
+
+ const closeButton = document.getElementById('modal-close-button');
+ closeButton.onclick = () => {
+ installModal.classList.add('hidden');
+ };
+ closeButton.classList = "hidden";
+ closeButton.textContent = '';
+
+ const ws = new WebSocket(`ws://${location.host}/api/apps/ws/apps-manager/${jobId}`);
+ ws.onmessage = (event) => {
+ try {
+ if (event.data.startsWith('{') && event.data.endsWith('}')) {
+ const data = JSON.parse(event.data);
+
+ if (data.status === "failed") {
+ closeButton.classList = "text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800";
+ closeButton.textContent = 'Close';
+ console.error(`Installation of ${appName} failed.`);
+ } else if (data.status === "done") {
+ closeButton.classList = "text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800";
+ closeButton.textContent = 'Install done';
+ console.log(`Installation of ${appName} completed.`);
+
+ }
+ }
+ else {
+ logsDiv.innerHTML += event.data + '\n';
+ logsDiv.scrollTop = logsDiv.scrollHeight;
+ }
+
+
+ } catch {
+ logsDiv.innerHTML += event.data + '\n';
+ logsDiv.scrollTop = logsDiv.scrollHeight;
+ }
+ };
+ ws.onclose = async () => {
+ hfAppsStore.isInstalling = false;
+ hfAppsStore.refreshAppList();
+ installedApps.refreshAppList();
+ };
+ },
+};
+
+
+window.addEventListener('load', async () => {
+ await hfAppsStore.refreshAppList();
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/daemon.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/daemon.js
new file mode 100644
index 0000000000000000000000000000000000000000..4bcb67c725d9dfb99e17d34d15a73b281c1baebc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/daemon.js
@@ -0,0 +1,169 @@
+
+
+
+const daemon = {
+ currentStatus: {
+ state: null,
+ },
+
+ start: async (wakeUp) => {
+ await fetch(`/api/daemon/start?wake_up=${wakeUp}`, {
+ method: 'POST',
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(async (data) => {
+ await daemon.checkStatusUpdate();
+ })
+ .catch((error) => {
+ console.error('Error starting daemon:', error);
+ });
+ },
+
+ stop: async (gotoSleep) => {
+ await fetch(`/api/daemon/stop?goto_sleep=${gotoSleep}`, {
+ method: 'POST',
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(async (data) => {
+ await daemon.checkStatusUpdate();
+ })
+ .catch((error) => {
+ console.error('Error stopping daemon:', error);
+ });
+ },
+
+ getStatus: async () => {
+ await fetch('/api/daemon/status')
+ .then((response) => response.json())
+ .then(async (data) => {
+ let currentState = daemon.currentStatus.state;
+ let newState = data.state || null;
+
+ daemon.currentStatus = data;
+
+ if (currentState === null || currentState !== newState) {
+ await daemon.updateUI();
+ }
+
+ })
+ .catch((error) => {
+ console.error('Error fetching daemon status:', error);
+ });
+ },
+
+ checkStatusUpdate: async (initialState) => {
+ await daemon.getStatus();
+
+ if (!initialState) {
+ initialState = daemon.currentStatus.state;
+ }
+
+ let currentState = daemon.currentStatus.state;
+
+ if (currentState !== "error" && (currentState === initialState || currentState === "starting" || currentState === "stopping")) {
+ setTimeout(() => {
+ daemon.checkStatusUpdate(initialState);
+ }, 500);
+ }
+ },
+
+ toggleSwitch: async () => {
+ const toggleDaemonSwitch = document.getElementById('daemon-toggle');
+
+ if (toggleDaemonSwitch.checked) {
+ console.log('Toggle switched ON. Starting daemon...');
+ await daemon.start(true);
+ } else {
+ console.log('Toggle switched OFF. Stopping daemon...');
+ await daemon.stop(true);
+ }
+
+ await daemon.updateToggle();
+ },
+
+ updateUI: async () => {
+ const daemonStatusAnim = document.getElementById('daemon-status-anim');
+ const toggleDaemonSwitch = document.getElementById('daemon-toggle');
+ const backendStatusIcon = document.getElementById('backend-status-icon');
+ const backendStatusText = document.getElementById('backend-status-text');
+
+ let daemonState = daemon.currentStatus.state;
+
+ toggleDaemonSwitch.disabled = false;
+ backendStatusIcon.classList.remove('bg-green-500', 'bg-yellow-500', 'bg-red-500');
+
+ if (daemonState === 'starting') {
+ // daemonStatusAnim.setAttribute('data', '/static/assets/reachy-mini-wake-up-animation.svg');
+ daemonStatusAnim.setAttribute('data', '/static/assets/awake-cartoon.svg');
+ toggleDaemonSwitch.disabled = true;
+ toggleDaemonSwitch.checked = true;
+ backendStatusIcon.classList.add('bg-yellow-500');
+ backendStatusText.textContent = 'Waking up...';
+ }
+ else if (daemonState === 'running') {
+ // daemonStatusAnim.setAttribute('data', '/static/assets/reachy-mini-awake.svg');
+ daemonStatusAnim.setAttribute('data', '/static/assets/awake-cartoon-static.svg');
+ toggleDaemonSwitch.checked = true;
+ backendStatusIcon.classList.add('bg-green-500');
+ backendStatusText.textContent = 'Up and ready';
+ }
+ else if (daemonState === 'stopping') {
+ // daemonStatusAnim.setAttribute('data', '/static/assets/reachy-mini-go-to-sleep-animation.svg');
+ daemonStatusAnim.setAttribute('data', '/static/assets/go-to-sleep-cartoon.svg');
+ toggleDaemonSwitch.disabled = true;
+ toggleDaemonSwitch.checked = false;
+ backendStatusIcon.classList.add('bg-yellow-500');
+ backendStatusText.textContent = 'Going to sleep...';
+ }
+ else if (daemonState === 'stopped' || daemonState === 'not_initialized') {
+ // daemonStatusAnim.setAttribute('data', '/static/assets/reachy-mini-sleeping.svg');
+ daemonStatusAnim.setAttribute('data', '/static/assets/reachy-mini-sleeping-static.svg');
+ toggleDaemonSwitch.checked = false;
+ backendStatusIcon.classList.add('bg-yellow-500');
+ backendStatusText.textContent = 'Stopped';
+ }
+ else if (daemonState === 'error') {
+ daemonStatusAnim.setAttribute('data', '/static/assets/no-wifi-cartoon.svg');
+ toggleDaemonSwitch.checked = false;
+ backendStatusIcon.classList.add('bg-red-500');
+ backendStatusText.textContent = 'Error occurred';
+
+ notificationCenter.showError(daemon.currentStatus.error);
+ }
+
+ await daemon.updateToggle();
+ },
+
+ updateToggle: async () => {
+ const toggle = document.getElementById('daemon-toggle');
+ const toggleSlider = document.getElementById('daemon-toggle-slider');
+ const toggleOnLabel = document.getElementById('daemon-toggle-on');
+ const toggleOffLabel = document.getElementById('daemon-toggle-off');
+
+ toggleSlider.classList.remove('hidden');
+
+ if (toggle.checked) {
+ toggleOnLabel.classList.remove('hidden');
+ toggleOffLabel.classList.add('hidden');
+ } else {
+ toggleOnLabel.classList.add('hidden');
+ toggleOffLabel.classList.remove('hidden');
+ }
+ },
+};
+
+
+window.addEventListener('load', async () => {
+ document.getElementById('daemon-toggle').onchange = daemon.toggleSwitch;
+ await daemon.getStatus();
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/health_check.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/health_check.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0514b2765eddf49ee35c3e990a89a5a34717891
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/health_check.js
@@ -0,0 +1,9 @@
+window.addEventListener("load", () => {
+ const healthCheckInterval = setInterval(async () => {
+ const response = await fetch("/health-check", { method: "POST" });
+ if (!response.ok) {
+ console.error("Health check failed");
+ clearInterval(healthCheckInterval);
+ }
+ }, 2500);
+});
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/move_player.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/move_player.js
new file mode 100644
index 0000000000000000000000000000000000000000..322a94e7e199936d9ce696362bb54a5fa44cad2f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/move_player.js
@@ -0,0 +1,132 @@
+const updateMoveItems = async (dataset) => {
+ fetch(`/api/move/recorded-move-datasets/list/${dataset}`)
+ .then(response => response.json())
+ .then(data => {
+ // Update the UI with the list of recorded moves
+ const moveSelectToggle = document.getElementById('move-select-toggle');
+ moveSelectToggle.innerHTML = '';
+
+ data.forEach(moveName => {
+ const option = document.createElement('option');
+ option.value = moveName;
+ option.textContent = moveName;
+ moveSelectToggle.appendChild(option);
+ });
+
+ })
+ .catch(error => {
+ console.error(`Error fetching recorded moves for dataset '${dataset}':`, error);
+ });
+};
+
+const movePlayer = {
+ playing: false,
+ currentMove: null,
+
+ playRecordedMove: async (dataset, move) => {
+ console.log(`Requesting play move '${move}' from dataset '${dataset}'`);
+
+ // movePlayer.playing = true;
+ // movePlayer.updateUI();
+
+ await fetch(`/api/move/play/recorded-move-dataset/${dataset}/${move}`, {
+ method: 'POST'
+ }).then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ }
+ ).then(data => {
+ movePlayer.currentMove = data.uuid;
+ }).catch(error => {
+ console.error(`Error playing move '${move}' from dataset '${dataset}':`, error);
+ // movePlayer.playing = false;
+ // movePlayer.currentMove = null;
+ // movePlayer.updateUI();
+ });
+ },
+
+ stopMove: async () => {
+ console.log(`Requesting stop of current move`);
+
+ await fetch(`/api/move/stop`, {
+ method: 'POST',
+ body: JSON.stringify({ uuid: movePlayer.currentMove }),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ });
+ },
+
+ updateUI: () => {
+ const movePlayBtn = document.getElementById('move-play-btn');
+ const moveStopBtn = document.getElementById('move-stop-btn');
+
+ if (movePlayer.playing) {
+ movePlayBtn.disabled = true;
+ moveStopBtn.disabled = false;
+ } else {
+ movePlayBtn.disabled = false;
+ moveStopBtn.disabled = true;
+ }
+ },
+
+ checkMoveStatus: async () => {
+ let ws = new WebSocket(`ws://${window.location.host}/api/move/ws/updates`);
+
+ ws.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+ if (data.type === 'move_started') {
+ movePlayer.playing = true;
+ movePlayer.currentMove = data.move_id;
+ }
+ else if (data.type === 'move_completed') {
+ movePlayer.playing = false;
+ movePlayer.currentMove = null;
+ }
+ else if (data.type === 'move_failed' || data.type === 'move_cancelled') {
+ movePlayer.playing = false;
+ movePlayer.currentMove = null;
+ }
+
+ movePlayer.updateUI();
+ };
+
+ ws.onclose = () => {
+ console.log('Move status WebSocket closed, reconnecting in 1 second...');
+ setTimeout(() => {
+ movePlayer.checkMoveStatus();
+ }, 1000);
+ };
+ }
+};
+
+window.addEventListener('DOMContentLoaded', (event) => {
+ const moveDatasetToggle = document.getElementById('move-dataset-toggle');
+
+ moveDatasetToggle.addEventListener('change', (_) => {
+ const selectedDataset = moveDatasetToggle.value;
+ updateMoveItems(selectedDataset);
+ });
+
+ const movePlayBtn = document.getElementById('move-play-btn');
+ movePlayBtn.addEventListener('click', async () => {
+ const selectedDataset = moveDatasetToggle.value;
+ const moveSelectToggle = document.getElementById('move-select-toggle');
+ const selectedMove = moveSelectToggle.value;
+
+ await movePlayer.playRecordedMove(selectedDataset, selectedMove);
+ });
+
+ const moveStopBtn = document.getElementById('move-stop-btn');
+ moveStopBtn.addEventListener('click', async () => {
+ await movePlayer.stopMove();
+ });
+
+ // Initialize move items on page load
+ updateMoveItems(moveDatasetToggle.value);
+
+ movePlayer.checkMoveStatus();
+ movePlayer.updateUI();
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/notification.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/notification.js
new file mode 100644
index 0000000000000000000000000000000000000000..69e119956273544332422e0ad25e397bcb44ead3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/notification.js
@@ -0,0 +1,41 @@
+const notificationCenter = {
+
+ showInfo: (message, duration = 5000, autoclose = true) => {
+ notificationCenter.showNotification(message, false, duration, autoclose);
+ },
+ showError: (message, duration = 5000, autoclose = false) => {
+ message = notificationCenter.errorTranslation(message);
+ notificationCenter.showNotification(message, true, duration, autoclose);
+ },
+
+ showNotification: (message, error, duration = 5000, autoclose = true) => {
+ document.getElementById('notification-message').textContent = message;
+ document.getElementById('notification-modal').style.display = 'block';
+
+ if (error) {
+ document.getElementById('notification-content').classList.add('error');
+ document.getElementById('notification-content').classList.remove('info');
+ } else {
+ document.getElementById('notification-content').classList.remove('error');
+ document.getElementById('notification-content').classList.add('info');
+ }
+
+ if (autoclose) {
+ setTimeout(notificationCenter.closeNotification, duration);
+ }
+ },
+
+ closeNotification: () => {
+ document.getElementById('notification-modal').style.display = 'none';
+ },
+
+ errorTranslation: (errorMessage) => {
+ if (errorMessage.includes('No Reachy Mini serial port found.')) {
+ return 'Reachy Mini not detected on USB. Please check that the USB cable is properly connected.';
+ }
+
+ console.log('No translation found for error message:', errorMessage);
+ return errorMessage;
+ },
+
+};
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/update.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/update.js
new file mode 100644
index 0000000000000000000000000000000000000000..a1e6812812dcb99af3c2e74ffc80f9244efdf2fc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/update.js
@@ -0,0 +1,132 @@
+const updateManager = {
+ busy: false,
+ preRelease: false,
+
+ checkForUpdate: async () => {
+ await updateManager.updateUI(); // Clear previous state
+
+ await fetch('/update/available?pre_release=' + updateManager.preRelease)
+ .then(async response => {
+ if (!response.ok) {
+ return false;
+ }
+ const data = await response.json();
+ await updateManager.updateUI(data);
+ }).catch(error => {
+ console.error('Error checking for updates:', error);
+ });
+ },
+
+ startUpdate: async () => {
+ if (updateManager.busy) {
+ console.warn('An update is already in progress.');
+ return;
+ }
+ updateManager.busy = true;
+
+ fetch('/update/start?pre_release=' + updateManager.preRelease, { method: 'POST' })
+ .then(response => {
+ if (response.ok) {
+ return response.json();
+ } else {
+ return response.json().then(data => {
+ throw new Error(data.detail || 'Error starting update');
+ });
+ }
+ })
+ .then(data => {
+ const jobId = data.job_id;
+ updateManager.connectLogsWebSocket(jobId);
+ })
+ .catch(error => {
+ console.error('Error triggering update:', error);
+ updateManager.busy = false;
+ });
+ },
+
+ connectLogsWebSocket: (jobId) => {
+ const updateModal = document.getElementById('update-modal');
+ const updateModalTitle = document.getElementById('update-modal-title');
+ const logsDiv = document.getElementById('update-logs');
+ const closeButton = document.getElementById('update-modal-close-button');
+
+ updateModalTitle.textContent = 'Updating...';
+
+ closeButton.onclick = () => {
+ updateModal.classList.add('hidden');
+ };
+
+ updateModal.classList.remove('hidden');
+
+ const ws = new WebSocket(`ws://${location.host}/update/ws/logs?job_id=${jobId}`);
+
+ ws.onmessage = (event) => {
+ // console.log('Update log:', event);
+ logsDiv.innerHTML += event.data + ' ';
+ logsDiv.scrollTop = logsDiv.scrollHeight;
+ };
+ ws.onclose = async () => {
+ console.log('Update logs WebSocket closed');
+ closeButton.classList.remove('hidden');
+ closeButton.textContent = 'Close';
+ updateModalTitle.textContent = 'Update Completed ✅';
+
+ updateManager.busy = false;
+ await updateManager.checkForUpdate();
+ };
+ },
+
+ updateUI: async (update) => {
+ // updateManager.updateMainPage(isUpdateAvailable);
+ updateManager.updateUpdatePage(update);
+ },
+
+ // updateMainPage: async (update) => {
+ // const daemonUpdateBtn = document.getElementById('daemon-update-btn');
+ // if (!daemonUpdateBtn) return;
+
+ // if (isUpdateAvailable) {
+ // daemonUpdateBtn.innerHTML = 'Update 1 ';
+ // } else {
+ // daemonUpdateBtn.innerHTML = 'Update';
+ // }
+ // },
+ updateUpdatePage: async (data) => {
+ const statusElem = document.getElementById('update-status');
+ if (!statusElem) return;
+
+ const currentVersionElem = document.getElementById('current-version');
+ const availableVersionElem = document.getElementById('available-version');
+ const availableVersionContainer = document.getElementById('available-version-container');
+ const startUpdateBtn = document.getElementById('start-update-btn');
+
+ if (!data || !data.update || !data.update.reachy_mini) {
+ statusElem.innerHTML = 'Checking for updates...';
+ if (currentVersionElem) currentVersionElem.textContent = '';
+ if (availableVersionElem) availableVersionElem.textContent = '';
+ return;
+ }
+
+ const updateInfo = data.update.reachy_mini;
+ const isUpdateAvailable = updateInfo.is_available;
+ const currentVersion = updateInfo.current_version || '-';
+ const availableVersion = updateInfo.available_version || '-';
+
+ if (currentVersionElem) currentVersionElem.textContent = `Current version: ${currentVersion}`;
+ if (availableVersionElem) availableVersionElem.textContent = `Available version: ${availableVersion}`;
+
+ if (isUpdateAvailable) {
+ statusElem.innerHTML = 'An update is available!';
+ if (availableVersionContainer) availableVersionContainer.classList.remove('hidden');
+ startUpdateBtn.classList.remove('hidden');
+ } else {
+ statusElem.innerHTML = 'Your system is up to date.';
+ if (availableVersionContainer) availableVersionContainer.classList.add('hidden');
+ startUpdateBtn.classList.add('hidden');
+ }
+ }
+};
+
+window.addEventListener('load', async () => {
+ await updateManager.checkForUpdate();
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/volume_control.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/volume_control.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b6fdbd05a9bfff354480385d304210794235488
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/volume_control.js
@@ -0,0 +1,252 @@
+const volumeControl = {
+ currentVolume: 50,
+ device: 'unknown',
+ platform: 'unknown',
+ isUpdating: false,
+
+ init: async () => {
+ const slider = document.getElementById('volume-slider');
+ const valueLabel = document.getElementById('volume-value');
+ const deviceInfo = document.getElementById('volume-device-info');
+
+ if (!slider || !valueLabel || !deviceInfo) {
+ console.warn('Volume control elements not found in DOM');
+ return;
+ }
+
+ try {
+ await volumeControl.loadCurrentVolume();
+ } catch (error) {
+ console.error('Error loading current volume:', error);
+ deviceInfo.textContent = 'Error loading volume';
+ }
+
+ slider.addEventListener('input', (e) => {
+ const v = Number(e.target.value);
+ valueLabel.textContent = String(v);
+ });
+
+ slider.addEventListener('change', async (e) => {
+ const newVolume = Number(e.target.value);
+ if (!Number.isFinite(newVolume)) return;
+ await volumeControl.setVolume(newVolume);
+ });
+ },
+
+ loadCurrentVolume: async () => {
+ try {
+ const response = await fetch('/api/volume/current');
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+
+ const volume = Number(data.volume);
+ if (!Number.isFinite(volume)) {
+ throw new Error('Invalid volume in response');
+ }
+
+ volumeControl.currentVolume = volume;
+ volumeControl.device = data.device ?? 'unknown';
+ volumeControl.platform = data.platform ?? 'unknown';
+
+ const slider = document.getElementById('volume-slider');
+ const valueLabel = document.getElementById('volume-value');
+ const deviceInfo = document.getElementById('volume-device-info');
+
+ if (slider) slider.value = String(volume);
+ if (valueLabel) valueLabel.textContent = String(volume);
+ if (deviceInfo) {
+ deviceInfo.textContent = `${volumeControl.platform} - ${volumeControl.device}`;
+ }
+
+ console.log('Loaded volume:', data);
+ } catch (error) {
+ console.error('Error loading current volume:', error);
+ throw error;
+ }
+ },
+
+ setVolume: async (volume) => {
+ if (!Number.isFinite(volume)) {
+ console.warn('Ignoring invalid volume:', volume);
+ return;
+ }
+
+ const safeVolume = Math.max(0, Math.min(100, volume));
+ if (volumeControl.isUpdating) {
+ console.log('Volume update already in progress, skipping...');
+ return;
+ }
+
+ volumeControl.isUpdating = true;
+ const slider = document.getElementById('volume-slider');
+
+ if (slider) slider.disabled = true;
+
+ try {
+ const response = await fetch('/api/volume/set', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ volume: safeVolume }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const serverVolume = Number(data.volume);
+
+ if (Number.isFinite(serverVolume)) {
+ volumeControl.currentVolume = serverVolume;
+ const s = document.getElementById('volume-slider');
+ const valueLabel = document.getElementById('volume-value');
+ if (s) s.value = String(serverVolume);
+ if (valueLabel) valueLabel.textContent = String(serverVolume);
+ }
+
+ console.log('Volume set to:', serverVolume);
+ } catch (error) {
+ console.error('Error setting volume:', error);
+ try {
+ await volumeControl.loadCurrentVolume();
+ } catch (loadError) {
+ console.error('Also failed to reload volume:', loadError);
+ }
+ } finally {
+ volumeControl.isUpdating = false;
+ const s = document.getElementById('volume-slider');
+ if (s) s.disabled = false;
+ }
+ },
+};
+
+const microphoneControl = {
+ currentVolume: 50,
+ device: 'unknown',
+ platform: 'unknown',
+ isUpdating: false,
+
+ init: async () => {
+ const slider = document.getElementById('microphone-slider');
+ const valueLabel = document.getElementById('microphone-value');
+ const deviceInfo = document.getElementById('microphone-device-info');
+
+ if (!slider || !valueLabel || !deviceInfo) {
+ console.warn('Microphone control elements not found in DOM');
+ return;
+ }
+
+ try {
+ await microphoneControl.loadCurrentVolume();
+ } catch (error) {
+ console.error('Error loading current microphone volume:', error);
+ deviceInfo.textContent = 'Error loading microphone';
+ }
+
+ slider.addEventListener('input', (e) => {
+ const v = Number(e.target.value);
+ valueLabel.textContent = String(v);
+ });
+
+ slider.addEventListener('change', async (e) => {
+ const newVolume = Number(e.target.value);
+ if (!Number.isFinite(newVolume)) return;
+ await microphoneControl.setVolume(newVolume);
+ });
+ },
+
+ loadCurrentVolume: async () => {
+ try {
+ const response = await fetch('/api/volume/microphone/current');
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+
+ const volume = Number(data.volume);
+ if (!Number.isFinite(volume)) {
+ throw new Error('Invalid microphone volume in response');
+ }
+
+ microphoneControl.currentVolume = volume;
+ microphoneControl.device = data.device ?? 'unknown';
+ microphoneControl.platform = data.platform ?? 'unknown';
+
+ const slider = document.getElementById('microphone-slider');
+ const valueLabel = document.getElementById('microphone-value');
+ const deviceInfo = document.getElementById('microphone-device-info');
+
+ if (slider) slider.value = String(volume);
+ if (valueLabel) valueLabel.textContent = String(volume);
+ if (deviceInfo) {
+ deviceInfo.textContent = `${microphoneControl.platform} - ${microphoneControl.device}`;
+ }
+
+ console.log('Loaded microphone volume:', data);
+ } catch (error) {
+ console.error('Error loading current microphone volume:', error);
+ throw error;
+ }
+ },
+
+ setVolume: async (volume) => {
+ if (!Number.isFinite(volume)) {
+ console.warn('Ignoring invalid microphone volume:', volume);
+ return;
+ }
+
+ const safeVolume = Math.max(0, Math.min(100, volume));
+ if (microphoneControl.isUpdating) {
+ console.log('Microphone volume update already in progress, skipping...');
+ return;
+ }
+
+ microphoneControl.isUpdating = true;
+ const slider = document.getElementById('microphone-slider');
+
+ if (slider) slider.disabled = true;
+
+ try {
+ const response = await fetch('/api/volume/microphone/set', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ volume: safeVolume }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const serverVolume = Number(data.volume);
+
+ if (Number.isFinite(serverVolume)) {
+ microphoneControl.currentVolume = serverVolume;
+ const s = document.getElementById('microphone-slider');
+ const valueLabel = document.getElementById('microphone-value');
+ if (s) s.value = String(serverVolume);
+ if (valueLabel) valueLabel.textContent = String(serverVolume);
+ }
+
+ console.log('Microphone volume set to:', serverVolume);
+ } catch (error) {
+ console.error('Error setting microphone volume:', error);
+ try {
+ await microphoneControl.loadCurrentVolume();
+ } catch (loadError) {
+ console.error('Also failed to reload microphone volume:', loadError);
+ }
+ } finally {
+ microphoneControl.isUpdating = false;
+ const s = document.getElementById('microphone-slider');
+ if (s) s.disabled = false;
+ }
+ },
+};
+
+window.addEventListener('DOMContentLoaded', () => {
+ volumeControl.init();
+ microphoneControl.init();
+});
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/wifi.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/wifi.js
new file mode 100644
index 0000000000000000000000000000000000000000..3a620a408228bb0ef19790612912e7a97eb0e87c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/static/js/wifi.js
@@ -0,0 +1,176 @@
+
+const getStatus = async () => {
+ return await fetch('/wifi/status')
+ .then(response => response.json())
+ .catch(error => {
+ console.error('Error fetching WiFi status:', error);
+ return { mode: 'error' };
+ });
+};
+
+const refreshStatus = async () => {
+ const status = await getStatus();
+ handleStatus(status);
+
+ await fetch('/wifi/error')
+ .then(response => response.json())
+ .then(data => {
+ if (data.error !== null) {
+ console.log('Error data:', data);
+ alert(`Error while trying to connect: ${data.error}.\n Switching back to hotspot mode.`);
+ fetch('/wifi/reset_error', { method: 'POST' });
+ }
+ })
+ .catch(error => {
+ console.error('Error fetching WiFi error:', error);
+ });
+};
+
+const scanAndListWifiNetworks = async () => {
+ await fetch('/wifi/scan_and_list', { method: 'POST' })
+ .then(response => response.json())
+ .then(data => {
+ const ssidSelect = document.getElementById('ssid');
+ data.forEach(ssid => {
+ const option = document.createElement('option');
+ option.value = ssid;
+ option.textContent = ssid;
+ ssidSelect.appendChild(option);
+ });
+ })
+ .catch(() => {
+ const ssidSelect = document.getElementById('ssid');
+ const option = document.createElement('option');
+ option.value = "";
+ option.textContent = "Unable to load networks";
+ ssidSelect.appendChild(option);
+ });
+};
+
+const connectToWifi = (_) => {
+ const ssid = document.getElementById('ssid').value;
+ const password = document.getElementById('password').value;
+
+ if (!ssid) {
+ alert('Please enter an SSID.');
+ return;
+ }
+
+ fetch(`/wifi/connect?ssid=${encodeURIComponent(ssid)}&password=${encodeURIComponent(password)}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(response => {
+ if (!response.ok) {
+ return response.json().then(errData => {
+ throw new Error(errData.detail || 'Failed to connect to WiFi');
+ });
+ }
+
+ // Clear the form fields
+ document.getElementById('ssid').value = '';
+ document.getElementById('password').value = '';
+
+ return response.json();
+ })
+ .then(data => {
+ handleStatus({ mode: 'busy' });
+ })
+ .catch(error => {
+ console.error('Error connecting to WiFi:', error);
+ alert(`Error connecting to WiFi: ${error.message}`);
+ });
+ return false; // Prevent form submission
+};
+
+let currentMode = null;
+
+const handleStatus = (status) => {
+ const statusDiv = document.getElementById('wifi-status');
+
+ const knownNetworksDiv = document.getElementById('known-networks');
+ const knownNetworksList = document.getElementById('known-networks-list');
+ knownNetworksDiv.classList.remove('hidden');
+
+ const mode = status.mode;
+
+ knownNetworksList.innerHTML = '';
+ if (status.known_networks !== undefined && Array.isArray(status.known_networks)) {
+ status.known_networks.forEach((network) => {
+ const li = document.createElement('li');
+ li.classList = 'flex flex-row items-center mb-1 gap-4 justify-left';
+
+ const nameSpan = document.createElement('span');
+ nameSpan.innerText = network;
+ li.appendChild(nameSpan);
+
+ // const removeBtn = document.createElement('span');
+ // removeBtn.innerText = ' (remove ❌)';
+ // removeBtn.style.cursor = 'pointer';
+ // removeBtn.title = 'Remove network';
+ // removeBtn.onclick = async () => {
+ // if (confirm(`Remove network '${network}'?`)) {
+ // removeNetwork(network);
+ // }
+ // };
+ // li.appendChild(removeBtn);
+
+ knownNetworksList.appendChild(li);
+ });
+ }
+
+ if (mode == 'hotspot') {
+ statusDiv.innerText = 'Hotspot mode active. 🔌';
+
+ } else if (mode == 'wlan') {
+ if (currentMode !== null && currentMode !== 'wlan') {
+ alert(`Successfully connected to WiFi network: ${status.connected_network} ✅`);
+ }
+
+ statusDiv.innerText = `Connected to WiFi (SSID: ${status.connected_network}). 📶`;
+
+ } else if (mode == 'disconnected') {
+ statusDiv.innerText = 'WiFi disconnected. ❌';
+ } else if (mode == 'busy') {
+ statusDiv.innerText = 'Changing your WiFi configuration... Please wait ⏳';
+ } else if (mode == 'error') {
+ statusDiv.innerText = 'Error connecting to WiFi. ⚠️';
+ } else {
+ console.warn(`Unknown status: ${status}`);
+ }
+
+ currentMode = mode;
+};
+
+const removeNetwork = async (ssid) => {
+ const status = await getStatus();
+
+ // TODO:
+ // if ssid !== status.connected_network:
+ // remove connection
+ // else:
+ // refresh nmcli? go back to hotspot if needed?
+};
+
+const cleanAndRefresh = async () => {
+ const statusDiv = document.getElementById('wifi-status');
+ statusDiv.innerText = 'Checking WiFi configuration...';
+
+ const knownNetworksDiv = document.getElementById('known-networks');
+ knownNetworksDiv.classList.add('hidden');
+
+ const addWifi = document.getElementById('add-wifi');
+ addWifi.classList.add('hidden');
+
+ await scanAndListWifiNetworks();
+ await refreshStatus();
+
+ addWifi.classList.remove('hidden');
+};
+
+window.addEventListener('load', async () => {
+ await cleanAndRefresh();
+ setInterval(refreshStatus, 1000);
+});
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/.gitignore b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b512c09d476623ff4bf8d0d63c29b784925dbdf8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/.gitignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/README.md b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3d7fed0f0946ebfdb2d5771b50867eaa7b0729ce
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/README.md
@@ -0,0 +1,15 @@
+# Generate static Tailwind CSS
+
+## Requirements
+
+(From this folder)
+
+```bash
+pnpm install -D tailwindcss @tailwindcss/cli
+```
+
+## Generate CSS
+
+```bash
+npx @tailwindcss/cli -i ./styles/app.css -o ../static/css/app.css
+```
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3f0c7643d855903a6fc9bc92e44f5e6dc185eda
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json
@@ -0,0 +1,1127 @@
+{
+ "name": "tailwindcss",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@tailwindcss/cli": "^4.1.16",
+ "tailwindcss": "^4.1.16"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
+ "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^1.0.3",
+ "is-glob": "^4.0.3",
+ "micromatch": "^4.0.5",
+ "node-addon-api": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.1",
+ "@parcel/watcher-darwin-arm64": "2.5.1",
+ "@parcel/watcher-darwin-x64": "2.5.1",
+ "@parcel/watcher-freebsd-x64": "2.5.1",
+ "@parcel/watcher-linux-arm-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm-musl": "2.5.1",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm64-musl": "2.5.1",
+ "@parcel/watcher-linux-x64-glibc": "2.5.1",
+ "@parcel/watcher-linux-x64-musl": "2.5.1",
+ "@parcel/watcher-win32-arm64": "2.5.1",
+ "@parcel/watcher-win32-ia32": "2.5.1",
+ "@parcel/watcher-win32-x64": "2.5.1"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+ "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+ "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+ "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+ "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+ "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+ "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+ "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+ "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+ "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+ "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+ "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+ "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+ "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@tailwindcss/cli": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.16.tgz",
+ "integrity": "sha512-dsnANPrh2ZooHyZ/8uJhc9ecpcYtufToc21NY09NS9vF16rxPCjJ8dP7TUAtPqlUJTHSmRkN2hCdoYQIlgh4fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@parcel/watcher": "^2.5.1",
+ "@tailwindcss/node": "4.1.16",
+ "@tailwindcss/oxide": "4.1.16",
+ "enhanced-resolve": "^5.18.3",
+ "mri": "^1.2.0",
+ "picocolors": "^1.1.1",
+ "tailwindcss": "4.1.16"
+ },
+ "bin": {
+ "tailwindcss": "dist/index.mjs"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz",
+ "integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.19",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.16"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz",
+ "integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.16",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.16",
+ "@tailwindcss/oxide-darwin-x64": "4.1.16",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.16",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.16",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.16",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.16",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.16",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.16"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.16.tgz",
+ "integrity": "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.16.tgz",
+ "integrity": "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.16.tgz",
+ "integrity": "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.16.tgz",
+ "integrity": "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.16.tgz",
+ "integrity": "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.16.tgz",
+ "integrity": "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.16.tgz",
+ "integrity": "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.16.tgz",
+ "integrity": "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.16.tgz",
+ "integrity": "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.16.tgz",
+ "integrity": "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.5.0",
+ "@emnapi/runtime": "^1.5.0",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.0.7",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz",
+ "integrity": "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.16.tgz",
+ "integrity": "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.3",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+ "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss/node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
+ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
+ "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ }
+ }
+}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package.json b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..9e9ee28e6ea80bd2ae7e80a7e1b5d22472b8f064
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/package.json
@@ -0,0 +1,6 @@
+{
+ "devDependencies": {
+ "@tailwindcss/cli": "^4.1.16",
+ "tailwindcss": "^4.1.16"
+ }
+}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1211930a125a4ee857a1a12a0f07ff01cd342fbe
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml
@@ -0,0 +1,608 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@tailwindcss/cli':
+ specifier: ^4.1.16
+ version: 4.1.16
+ tailwindcss:
+ specifier: ^4.1.16
+ version: 4.1.16
+
+packages:
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+
+ '@tailwindcss/cli@4.1.16':
+ resolution: {integrity: sha512-dsnANPrh2ZooHyZ/8uJhc9ecpcYtufToc21NY09NS9vF16rxPCjJ8dP7TUAtPqlUJTHSmRkN2hCdoYQIlgh4fw==}
+ hasBin: true
+
+ '@tailwindcss/node@4.1.16':
+ resolution: {integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.16':
+ resolution: {integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
+ resolution: {integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
+ resolution: {integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
+ resolution: {integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
+ resolution: {integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
+ resolution: {integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
+ resolution: {integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
+ resolution: {integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
+ resolution: {integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
+ resolution: {integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
+ resolution: {integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
+ resolution: {integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.16':
+ resolution: {integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==}
+ engines: {node: '>= 10'}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ enhanced-resolve@5.18.3:
+ resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+ engines: {node: '>=10.13.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ lightningcss-android-arm64@1.30.2:
+ resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.30.2:
+ resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.2:
+ resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.2:
+ resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.2:
+ resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.2:
+ resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
+ engines: {node: '>= 12.0.0'}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ tailwindcss@4.1.16:
+ resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==}
+
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ engines: {node: '>=6'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+snapshots:
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+
+ '@tailwindcss/cli@4.1.16':
+ dependencies:
+ '@parcel/watcher': 2.5.1
+ '@tailwindcss/node': 4.1.16
+ '@tailwindcss/oxide': 4.1.16
+ enhanced-resolve: 5.18.3
+ mri: 1.2.0
+ picocolors: 1.1.1
+ tailwindcss: 4.1.16
+
+ '@tailwindcss/node@4.1.16':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.18.3
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.16
+
+ '@tailwindcss/oxide-android-arm64@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.16':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-x64': 4.1.16
+ '@tailwindcss/oxide-freebsd-x64': 4.1.16
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.16
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.16
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.16
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.16
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.16
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ detect-libc@1.0.3: {}
+
+ detect-libc@2.1.2: {}
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ graceful-fs@4.2.11: {}
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ jiti@2.6.1: {}
+
+ lightningcss-android-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ optional: true
+
+ lightningcss@1.30.2:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.30.2
+ lightningcss-darwin-arm64: 1.30.2
+ lightningcss-darwin-x64: 1.30.2
+ lightningcss-freebsd-x64: 1.30.2
+ lightningcss-linux-arm-gnueabihf: 1.30.2
+ lightningcss-linux-arm64-gnu: 1.30.2
+ lightningcss-linux-arm64-musl: 1.30.2
+ lightningcss-linux-x64-gnu: 1.30.2
+ lightningcss-linux-x64-musl: 1.30.2
+ lightningcss-win32-arm64-msvc: 1.30.2
+ lightningcss-win32-x64-msvc: 1.30.2
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mri@1.2.0: {}
+
+ node-addon-api@7.1.1: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ source-map-js@1.2.1: {}
+
+ tailwindcss@4.1.16: {}
+
+ tapable@2.3.0: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..33ad091d26d8a9dc95ebdf616e217d985ec215b8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css
new file mode 100644
index 0000000000000000000000000000000000000000..8c28fb1018a0aceb3fbb555024e0eeef4da96c42
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css
@@ -0,0 +1,185 @@
+@import "tailwindcss";
+
+@source "../../templates/**/*.html";
+@source "../../static/js/**/*.js";
+
+@font-face {
+ font-family: 'Asap';
+ src: url('/static/fonts/Asap-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Asap';
+ src: url('/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Archivo';
+ src: url('/static/fonts/Archivo-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Archivo';
+ src: url('/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf') format('truetype');
+ font-weight: 100 900;
+ font-style: italic;
+}
+
+
+
+html {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ font-family: 'Archivo', 'Asap', sans-serif;
+}
+
+.daemon-status-title {
+ font-size: 36px;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+}
+
+.daemon-status-collapse {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 0.7);
+}
+
+/* APPs styles */
+
+.app-section {
+ border-radius: 8px;
+ border-width: 1px;
+ padding: 16px;
+ gap: 16px;
+ background: #FFFFFF;
+}
+
+.installed-app-title {
+ font-weight: 600;
+}
+
+/* Hugging Face App Store Styles */
+
+.app-section-title {
+ font-weight: 500;
+ font-size: 24px;
+ letter-spacing: 0px;
+}
+
+.app-list-item {
+ padding: 4px;
+}
+
+.hf-app-icon {
+ width: 42px;
+ height: 42px;
+ border-radius: 10px;
+ text-align: center;
+ font-size: 31px;
+ background-color: rgba(0, 0, 0, 0.1);
+
+}
+
+.hf-app-title {
+ font-weight: 500;
+ font-size: 18px;
+}
+
+.hf-app-description {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 75%;
+ color: rgba(0, 0, 0, 0.4);
+}
+
+.hf-app-install-button {
+ font-weight: 500;
+ font-size: 20px;
+ font-style: Bold;
+ text-align: center;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.wifi-status {
+ font-family: 'Asap', sans-serif;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.9);
+
+}
+
+.wifi-status-info {
+ font-family: 'Asap', sans-serif;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 0.7);
+}
+
+
+.notification-modal {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ z-index: 9999;
+ min-width: 300px;
+ max-width: 400px;
+ background: #fff;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
+ border-radius: 8px;
+ display: none;
+ animation: fadeInUp 0.3s;
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(40px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.notification-content {
+ padding: 20px 28px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+/* Notification variants */
+.notification-content.info {
+ background: #fff;
+ border: 1px solid #e0e0e0;
+}
+
+.notification-content.error {
+ background: #ffeaea;
+ border: 1px solid #ff4d4f;
+ color: #b71c1c;
+}
+
+
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 1.5em;
+ cursor: pointer;
+ color: #888;
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..e5d481b84c2f2a5158ad670500d8279864a51178
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js
@@ -0,0 +1,11 @@
+module.exports = {
+ purge: [],
+ darkMode: false, // or 'media' or 'class'
+ theme: {
+ extend: {},
+ },
+ variants: {
+ extend: {},
+ },
+ plugins: [],
+}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/base.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..06f7fb80f8800acfcbaa51204f8eb45ba2bbc5e1
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/base.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ {% block title %}Reachy Mini dashboard{% endblock %}
+
+
+ {% block extra_js %}{% endblock %}
+
+
+
+
+
+
+ {% block content %}{% endblock %}
+
+
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/index.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..36e31060c328409894d0f408477c9b19d81fa7fc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/index.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+ {% include "sections/daemon.html" %}
+ {% include "sections/apps.html" %}
+ {% include "sections/appstore.html" %}
+ {% include "sections/notification.html" %}
+
+{% endblock %}
+
+{% block extra_js %}
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/apps.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/apps.html
new file mode 100644
index 0000000000000000000000000000000000000000..d1b4601ea1a537888fcd6e757fc4adf4ab64d82e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/apps.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/appstore.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/appstore.html
new file mode 100644
index 0000000000000000000000000000000000000000..2bc25425c2a4a4ea688268f541785d5fadc04cc0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/appstore.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Install from 🤗 Hugging Face
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/daemon.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/daemon.html
new file mode 100644
index 0000000000000000000000000000000000000000..262358448aa6058f864a9ae45cbe5acf584a678c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/daemon.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
Reachy Mini
+
+
+
+
+
+ ON
+
+ OFF
+
+
+
+
+
+
+
+
+
+ {% include 'sections/move_player.html' %}
+
+ {% if args.wireless_version %}
+
+ {% endif %}
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/move_player.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/move_player.html
new file mode 100644
index 0000000000000000000000000000000000000000..941e6843048a9dea282df7bc403d9af23e80b000
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/move_player.html
@@ -0,0 +1,121 @@
+
+
+
+
+ Dances
+ Emotions
+
+
+
+
+
+
+
+
+
Volume control
+
Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
50
+
+
+
+
+
+ Advanced settings
+
+
+
+
+
+
Microphone
+
Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
50
+
+
+
+
+
+
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/notification.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/notification.html
new file mode 100644
index 0000000000000000000000000000000000000000..97dd7b9ea461752ad181516368b3d1f31d172fd3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/notification.html
@@ -0,0 +1,19 @@
+
+
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/update.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/update.html
new file mode 100644
index 0000000000000000000000000000000000000000..e42595a4442f989dcfb6a356b978e89f7bb12768
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/update.html
@@ -0,0 +1,54 @@
+
+
Reachy Mini Update
+
+
+ Check for Updates
+
+
+ pre-release
+
+
+
+
+
+
+
+
+ Start Update
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Close
+
+
+
+
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/wifi.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/wifi.html
new file mode 100644
index 0000000000000000000000000000000000000000..9d55b418e326c134008b8ec93060902d813e0d54
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/sections/wifi.html
@@ -0,0 +1,46 @@
+
+
+
Setup your WiFi
+
+ 🔄
+
+
+
+
+
Checking WiFi configuration...
+
+
+
+
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/settings.html b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/settings.html
new file mode 100644
index 0000000000000000000000000000000000000000..42c83981a3396116e934dc7670cfcec55b7ffcbe
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dashboard/templates/settings.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+ {% include "sections/update.html" %}
+ {% include "sections/wifi.html" %}
+
+{% endblock %}
+
+{% block extra_js %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/dependencies.py b/reachy_mini/src/reachy_mini/daemon/app/dependencies.py
new file mode 100644
index 0000000000000000000000000000000000000000..8299f7765ee0c17be1da4c12c6e110db853e933b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/dependencies.py
@@ -0,0 +1,41 @@
+"""FastAPI common request dependencies."""
+
+from fastapi import HTTPException, Request, WebSocket
+
+from ...apps.manager import AppManager
+from ..backend.abstract import Backend
+from ..daemon import Daemon
+
+
+def get_daemon(request: Request) -> Daemon:
+ """Get the daemon as request dependency."""
+ assert isinstance(request.app.state.daemon, Daemon)
+ return request.app.state.daemon
+
+
+def get_backend(request: Request) -> Backend:
+ """Get the backend as request dependency."""
+ backend = request.app.state.daemon.backend
+
+ if backend is None or not backend.ready.is_set():
+ raise HTTPException(status_code=503, detail="Backend not running")
+
+ assert isinstance(backend, Backend)
+ return backend
+
+
+def get_app_manager(request: Request) -> "AppManager":
+ """Get the app manager as request dependency."""
+ assert isinstance(request.app.state.app_manager, AppManager)
+ return request.app.state.app_manager
+
+
+def ws_get_backend(websocket: WebSocket) -> Backend:
+ """Get the backend as websocket dependency."""
+ backend = websocket.app.state.daemon.backend
+
+ if backend is None or not backend.ready.is_set():
+ raise HTTPException(status_code=503, detail="Backend not running")
+
+ assert isinstance(backend, Backend)
+ return backend
diff --git a/reachy_mini/src/reachy_mini/daemon/app/main.py b/reachy_mini/src/reachy_mini/daemon/app/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4cfe0be091293832b6f9470f89841559a7b24ce
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/main.py
@@ -0,0 +1,347 @@
+"""Daemon entry point for the Reachy Mini robot.
+
+This script serves as the command-line interface (CLI) entry point for the Reachy Mini daemon.
+It initializes the daemon with specified parameters such as simulation mode, serial port,
+scene to load, and logging level. The daemon runs indefinitely, handling requests and
+managing the robot's state.
+
+"""
+
+import argparse
+import asyncio
+import logging
+from contextlib import asynccontextmanager
+from dataclasses import dataclass
+from pathlib import Path
+from typing import AsyncGenerator
+
+import uvicorn
+from fastapi import APIRouter, FastAPI, Request
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import HTMLResponse
+from fastapi.staticfiles import StaticFiles
+from fastapi.templating import Jinja2Templates
+
+from reachy_mini.apps.manager import AppManager
+from reachy_mini.daemon.app.routers import (
+ apps,
+ daemon,
+ kinematics,
+ motors,
+ move,
+ state,
+ volume,
+)
+from reachy_mini.daemon.daemon import Daemon
+
+
+@dataclass
+class Args:
+ """Arguments for configuring the Reachy Mini daemon."""
+
+ log_level: str = "INFO"
+
+ wireless_version: bool = False
+
+ serialport: str = "auto"
+ hardware_config_filepath: str | None = None
+
+ sim: bool = False
+ scene: str = "empty"
+ headless: bool = False
+
+ kinematics_engine: str = "AnalyticalKinematics"
+ check_collision: bool = False
+
+ autostart: bool = True
+ timeout_health_check: float | None = None
+
+ wake_up_on_start: bool = True
+ goto_sleep_on_stop: bool = True
+
+ fastapi_host: str = "0.0.0.0"
+ fastapi_port: int = 8000
+
+ localhost_only: bool | None = None
+
+
+def create_app(args: Args, health_check_event: asyncio.Event | None = None) -> FastAPI:
+ """Create and configure the FastAPI application."""
+ localhost_only = (
+ args.localhost_only
+ if args.localhost_only is not None
+ else (False if args.wireless_version else True)
+ )
+
+ @asynccontextmanager
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
+ """Lifespan context manager for the FastAPI application."""
+ args = app.state.args # type: Args
+
+ if args.autostart:
+ await app.state.daemon.start(
+ serialport=args.serialport,
+ sim=args.sim,
+ scene=args.scene,
+ headless=args.headless,
+ kinematics_engine=args.kinematics_engine,
+ check_collision=args.check_collision,
+ wake_up_on_start=args.wake_up_on_start,
+ localhost_only=localhost_only,
+ hardware_config_filepath=args.hardware_config_filepath,
+ )
+ yield
+ await app.state.app_manager.close()
+ await app.state.daemon.stop(
+ goto_sleep_on_stop=args.goto_sleep_on_stop,
+ )
+
+ app = FastAPI(
+ lifespan=lifespan,
+ )
+
+ app.state.args = args
+ app.state.daemon = Daemon(wireless_version=args.wireless_version)
+ app.state.app_manager = AppManager()
+
+ router = APIRouter(prefix="/api")
+ router.include_router(apps.router)
+ router.include_router(daemon.router)
+ router.include_router(kinematics.router)
+ router.include_router(motors.router)
+ router.include_router(move.router)
+ router.include_router(state.router)
+ router.include_router(volume.router)
+
+ if args.wireless_version:
+ from .routers import update, wifi_config
+
+ app.include_router(update.router)
+ app.include_router(wifi_config.router)
+
+ app.include_router(router)
+
+ if health_check_event is not None:
+
+ @app.post("/health-check")
+ async def health_check() -> dict[str, str]:
+ """Health check endpoint to reset the health check timer."""
+ health_check_event.set()
+ return {"status": "ok"}
+
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # or restrict to your HF domain
+ allow_methods=["*"],
+ allow_headers=["*"],
+ )
+
+ STATIC_DIR = Path(__file__).parent / "dashboard" / "static"
+ TEMPLATES_DIR = Path(__file__).parent / "dashboard" / "templates"
+
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
+ templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
+
+ @app.get("/")
+ async def dashboard(request: Request) -> HTMLResponse:
+ """Render the dashboard."""
+ return templates.TemplateResponse(
+ "index.html", {"request": request, "args": args}
+ )
+
+ if args.wireless_version:
+
+ @app.get("/settings")
+ async def settings(request: Request) -> HTMLResponse:
+ """Render the settings page."""
+ return templates.TemplateResponse("settings.html", {"request": request})
+
+ return app
+
+
+def run_app(args: Args) -> None:
+ """Run the FastAPI app with Uvicorn."""
+ logging.basicConfig(level=logging.INFO)
+
+ health_check_event = asyncio.Event()
+ app = create_app(args, health_check_event)
+
+ config = uvicorn.Config(app, host=args.fastapi_host, port=args.fastapi_port)
+ server = uvicorn.Server(config)
+
+ async def health_check_timeout(timeout_seconds: float) -> None:
+ while True:
+ try:
+ await asyncio.wait_for(
+ health_check_event.wait(),
+ timeout=timeout_seconds,
+ )
+ health_check_event.clear()
+ except asyncio.TimeoutError:
+ logging.warning("Health check timeout reached, stopping app.")
+ server.should_exit = True
+ break
+
+ loop = asyncio.get_event_loop()
+ if args.timeout_health_check is not None:
+ loop.create_task(health_check_timeout(args.timeout_health_check))
+
+ try:
+ loop.run_until_complete(server.serve())
+ except KeyboardInterrupt:
+ logging.info("Received Ctrl-C, shutting down gracefully.")
+ finally:
+ # Optional: additional cleanup here
+ pass
+
+
+def main() -> None:
+ """Run the FastAPI app with Uvicorn."""
+ default_args = Args()
+
+ parser = argparse.ArgumentParser(description="Run the Reachy Mini daemon.")
+ parser.add_argument(
+ "--wireless-version",
+ action="store_true",
+ default=default_args.wireless_version,
+ help="Use the wireless version of Reachy Mini (default: False).",
+ )
+
+ # Real robot mode
+ parser.add_argument(
+ "-p",
+ "--serialport",
+ type=str,
+ default=default_args.serialport,
+ help="Serial port for real motors (default: will try to automatically find the port).",
+ )
+ default_hw_config_path = str(
+ (
+ Path(__file__).parent.parent.parent
+ / "assets"
+ / "config"
+ / "hardware_config.yaml"
+ ).resolve()
+ )
+ parser.add_argument(
+ "--hardware-config-filepath",
+ type=str,
+ default=default_hw_config_path,
+ help=f"Path to the hardware configuration YAML file (default: {default_hw_config_path}).",
+ )
+ # Simulation mode
+ parser.add_argument(
+ "--sim",
+ action="store_true",
+ default=default_args.sim,
+ help="Run in simulation mode using Mujoco.",
+ )
+ parser.add_argument(
+ "--scene",
+ type=str,
+ default=default_args.scene,
+ help="Name of the scene to load (default: empty)",
+ )
+ parser.add_argument(
+ "--headless",
+ action="store_true",
+ default=default_args.headless,
+ help="Run the daemon in headless mode (default: False).",
+ )
+ # Daemon options
+ parser.add_argument(
+ "--autostart",
+ action="store_true",
+ default=default_args.autostart,
+ help="Automatically start the daemon on launch (default: True).",
+ )
+ parser.add_argument(
+ "--no-autostart",
+ action="store_false",
+ dest="autostart",
+ help="Do not automatically start the daemon on launch (default: False).",
+ )
+ parser.add_argument(
+ "--timeout-health-check",
+ type=float,
+ default=None,
+ help="Set the health check timeout in seconds (default: None).",
+ )
+ parser.add_argument(
+ "--wake-up-on-start",
+ action="store_true",
+ default=default_args.wake_up_on_start,
+ help="Wake up the robot on daemon start (default: True).",
+ )
+ parser.add_argument(
+ "--no-wake-up-on-start",
+ action="store_false",
+ dest="wake_up_on_start",
+ help="Do not wake up the robot on daemon start (default: False).",
+ )
+ parser.add_argument(
+ "--goto-sleep-on-stop",
+ action="store_true",
+ default=default_args.goto_sleep_on_stop,
+ help="Put the robot to sleep on daemon stop (default: True).",
+ )
+ parser.add_argument(
+ "--no-goto-sleep-on-stop",
+ action="store_false",
+ dest="goto_sleep_on_stop",
+ help="Do not put the robot to sleep on daemon stop (default: False).",
+ )
+ # Zenoh server options
+ parser.add_argument(
+ "--localhost-only",
+ action="store_true",
+ default=default_args.localhost_only,
+ help="Restrict the server to localhost only (default: True).",
+ )
+ parser.add_argument(
+ "--no-localhost-only",
+ action="store_false",
+ dest="localhost_only",
+ help="Allow the server to listen on all interfaces (default: False).",
+ )
+ # Kinematics options
+ parser.add_argument(
+ "--check-collision",
+ action="store_true",
+ default=default_args.check_collision,
+ help="Enable collision checking (default: False).",
+ )
+
+ parser.add_argument(
+ "--kinematics-engine",
+ type=str,
+ default=default_args.kinematics_engine,
+ choices=["Placo", "NN", "AnalyticalKinematics"],
+ help="Set the kinematics engine (default: AnalyticalKinematics).",
+ )
+ # FastAPI server options
+ parser.add_argument(
+ "--fastapi-host",
+ type=str,
+ default=default_args.fastapi_host,
+ )
+ parser.add_argument(
+ "--fastapi-port",
+ type=int,
+ default=default_args.fastapi_port,
+ )
+ # Logging options
+ parser.add_argument(
+ "--log-level",
+ type=str,
+ default=default_args.log_level,
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
+ help="Set the logging level (default: INFO).",
+ )
+
+ args = parser.parse_args()
+ run_app(Args(**vars(args)))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/models.py b/reachy_mini/src/reachy_mini/daemon/app/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..19a5310afc6431dc76db6e399701f62bc17826d8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/models.py
@@ -0,0 +1,149 @@
+"""Common pydantic models definitions."""
+
+from datetime import datetime
+
+import numpy as np
+from numpy.typing import NDArray
+from pydantic import BaseModel
+from scipy.spatial.transform import Rotation as R
+
+from reachy_mini.daemon.backend.abstract import MotorControlMode
+
+
+class Matrix4x4Pose(BaseModel):
+ """Represent a 3D pose by its 4x4 transformation matrix (translation is expressed in meters)."""
+
+ m: tuple[
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ ]
+
+ @classmethod
+ def from_pose_array(cls, arr: NDArray[np.float64]) -> "Matrix4x4Pose":
+ """Create a Matrix4x4 pose representation from a 4x4 pose array."""
+ assert arr.shape == (4, 4), "Array must be of shape (4, 4)"
+ m: tuple[
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ float,
+ ] = tuple(arr.flatten().tolist()) # type: ignore [assignment]
+ return cls(m=m)
+
+ def to_pose_array(self) -> NDArray[np.float64]:
+ """Convert the Matrix4x4Pose to a 4x4 numpy array."""
+ return np.array(self.m).reshape((4, 4))
+
+
+class XYZRPYPose(BaseModel):
+ """Represent a 3D pose using position (x, y, z) in meters and orientation (roll, pitch, yaw) angles in radians."""
+
+ x: float = 0.0
+ y: float = 0.0
+ z: float = 0.0
+ roll: float = 0.0
+ pitch: float = 0.0
+ yaw: float = 0.0
+
+ @classmethod
+ def from_pose_array(cls, arr: NDArray[np.float64]) -> "XYZRPYPose":
+ """Create an XYZRPYPose representation from a 4x4 pose array."""
+ assert arr.shape == (4, 4), "Array must be of shape (4, 4)"
+
+ x, y, z = arr[0, 3], arr[1, 3], arr[2, 3]
+ roll, pitch, yaw = R.from_matrix(arr[:3, :3]).as_euler("xyz")
+
+ return cls(
+ x=x,
+ y=y,
+ z=z,
+ roll=roll,
+ pitch=pitch,
+ yaw=yaw,
+ )
+
+ def to_pose_array(self) -> NDArray[np.float64]:
+ """Convert the XYZRPYPose to a 4x4 numpy array."""
+ rotation = R.from_euler("xyz", [self.roll, self.pitch, self.yaw])
+ pose_matrix = np.eye(4)
+ pose_matrix[:3, 3] = [self.x, self.y, self.z]
+ pose_matrix[:3, :3] = rotation.as_matrix()
+ return pose_matrix
+
+
+AnyPose = XYZRPYPose | Matrix4x4Pose
+
+
+def as_any_pose(pose: NDArray[np.float64], use_matrix: bool) -> AnyPose:
+ """Convert a numpy array to an AnyPose representation."""
+ return (
+ Matrix4x4Pose.from_pose_array(pose)
+ if use_matrix
+ else XYZRPYPose.from_pose_array(pose)
+ )
+
+
+class FullBodyTarget(BaseModel):
+ """Represent the full body including the head pose and the joints for antennas."""
+
+ target_head_pose: AnyPose | None = None
+ target_antennas: tuple[float, float] | None = None
+ target_body_yaw: float | None = None
+ timestamp: datetime | None = None
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [
+ {
+ "target_head_pose": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "roll": 0.0,
+ "pitch": 0.0,
+ "yaw": 0.0,
+ },
+ "target_antennas": [0.0, 0.0],
+ "target_body_yaw": 0.0,
+ }
+ ]
+ }
+ }
+
+
+class FullState(BaseModel):
+ """Represent the full state of the robot including all joint positions and poses."""
+
+ control_mode: MotorControlMode | None = None
+ head_pose: AnyPose | None = None
+ head_joints: list[float] | None = None
+ body_yaw: float | None = None
+ antennas_position: list[float] | None = None
+ timestamp: datetime | None = None
+ passive_joints: list[float] | None = None
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/apps.py b/reachy_mini/src/reachy_mini/daemon/app/routers/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..163b58f5473b15ce93f608f823813f55d8baba07
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/apps.py
@@ -0,0 +1,114 @@
+"""Apps router for apps management."""
+
+from fastapi import (
+ APIRouter,
+ Depends,
+ HTTPException,
+ WebSocket,
+)
+
+from reachy_mini.apps import AppInfo, SourceKind
+from reachy_mini.apps.manager import AppManager, AppStatus
+from reachy_mini.daemon.app import bg_job_register
+from reachy_mini.daemon.app.dependencies import get_app_manager
+
+router = APIRouter(prefix="/apps")
+
+
+@router.get("/list-available/{source_kind}")
+async def list_available_apps(
+ source_kind: SourceKind,
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> list[AppInfo]:
+ """List available apps (including not installed)."""
+ return await app_manager.list_available_apps(source_kind)
+
+
+@router.get("/list-available")
+async def list_all_available_apps(
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> list[AppInfo]:
+ """List all available apps (including not installed)."""
+ return await app_manager.list_all_available_apps()
+
+
+@router.post("/install")
+async def install_app(
+ app_info: AppInfo,
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> dict[str, str]:
+ """Install a new app by its info (background, returns job_id)."""
+ job_id = bg_job_register.run_command(
+ "install", app_manager.install_new_app, app_info
+ )
+ return {"job_id": job_id}
+
+
+@router.post("/remove/{app_name}")
+async def remove_app(
+ app_name: str,
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> dict[str, str]:
+ """Remove an installed app by its name (background, returns job_id)."""
+ job_id = bg_job_register.run_command("remove", app_manager.remove_app, app_name)
+ return {"job_id": job_id}
+
+
+@router.get("/job-status/{job_id}")
+async def job_status(job_id: str) -> bg_job_register.JobInfo:
+ """Get status/logs for a job."""
+ try:
+ return bg_job_register.get_info(job_id)
+ except Exception as e:
+ raise HTTPException(status_code=404, detail=str(e))
+
+
+# WebSocket route for live job status/logs
+@router.websocket("/ws/apps-manager/{job_id}")
+async def ws_apps_manager(websocket: WebSocket, job_id: str) -> None:
+ """WebSocket route to stream live job status/logs for a job, sending updates as soon as new logs are available."""
+ await websocket.accept()
+ await bg_job_register.ws_poll_info(websocket, job_id)
+ await websocket.close()
+
+
+@router.post("/start-app/{app_name}")
+async def start_app(
+ app_name: str,
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> AppStatus:
+ """Start an app by its name."""
+ try:
+ return await app_manager.start_app(app_name)
+ except RuntimeError as e:
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.post("/restart-current-app")
+async def restart_app(
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> AppStatus:
+ """Restart the currently running app."""
+ try:
+ return await app_manager.restart_current_app()
+ except RuntimeError as e:
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.post("/stop-current-app")
+async def stop_app(
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> None:
+ """Stop the currently running app."""
+ try:
+ return await app_manager.stop_current_app()
+ except RuntimeError as e:
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.get("/current-app-status")
+async def current_app_status(
+ app_manager: "AppManager" = Depends(get_app_manager),
+) -> AppStatus | None:
+ """Get the status of the currently running app, if any."""
+ return await app_manager.current_app_status()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/daemon.py b/reachy_mini/src/reachy_mini/daemon/app/routers/daemon.py
new file mode 100644
index 0000000000000000000000000000000000000000..3de3be57a431b7029edcb5b2e98b5e38e3c8fc6d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/daemon.py
@@ -0,0 +1,82 @@
+"""Daemon-related API routes."""
+
+import logging
+import threading
+
+from fastapi import APIRouter, Depends, HTTPException, Request
+
+from reachy_mini.daemon.app import bg_job_register
+
+from ...daemon import Daemon, DaemonStatus
+from ..dependencies import get_daemon
+
+router = APIRouter(
+ prefix="/daemon",
+)
+busy_lock = threading.Lock()
+
+
+@router.post("/start")
+async def start_daemon(
+ request: Request,
+ wake_up: bool,
+ daemon: Daemon = Depends(get_daemon),
+) -> dict[str, str]:
+ """Start the daemon."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=409, detail="Daemon is busy.")
+
+ async def start(logger: logging.Logger) -> None:
+ with busy_lock:
+ await daemon.start(
+ sim=request.app.state.args.sim,
+ serialport=request.app.state.args.serialport,
+ scene=request.app.state.args.scene,
+ localhost_only=request.app.state.args.localhost_only,
+ wake_up_on_start=wake_up,
+ check_collision=request.app.state.args.check_collision,
+ kinematics_engine=request.app.state.args.kinematics_engine,
+ headless=request.app.state.args.headless,
+ hardware_config_filepath=request.app.state.args.hardware_config_filepath,
+ )
+
+ job_id = bg_job_register.run_command("daemon-start", start)
+ return {"job_id": job_id}
+
+
+@router.post("/stop")
+async def stop_daemon(
+ goto_sleep: bool, daemon: Daemon = Depends(get_daemon)
+) -> dict[str, str]:
+ """Stop the daemon, optionally putting the robot to sleep."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=409, detail="Daemon is busy.")
+
+ async def stop(logger: logging.Logger) -> None:
+ with busy_lock:
+ await daemon.stop(goto_sleep_on_stop=goto_sleep)
+
+ job_id = bg_job_register.run_command("daemon-stop", stop)
+ return {"job_id": job_id}
+
+
+@router.post("/restart")
+async def restart_daemon(
+ request: Request, daemon: Daemon = Depends(get_daemon)
+) -> dict[str, str]:
+ """Restart the daemon."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=409, detail="Daemon is busy.")
+
+ async def restart(logger: logging.Logger) -> None:
+ with busy_lock:
+ await daemon.restart()
+
+ job_id = bg_job_register.run_command("daemon-restart", restart)
+ return {"job_id": job_id}
+
+
+@router.get("/status")
+async def get_daemon_status(daemon: Daemon = Depends(get_daemon)) -> DaemonStatus:
+ """Get the current status of the daemon."""
+ return daemon.status()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/kinematics.py b/reachy_mini/src/reachy_mini/daemon/app/routers/kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..71834eb17ed6e895309d13e267097a33e45ba7c3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/kinematics.py
@@ -0,0 +1,57 @@
+"""Kinematics router for handling kinematics-related requests.
+
+This module defines the API endpoints for interacting with the kinematics
+subsystem of the robot. It provides endpoints for retrieving URDF representation,
+and other kinematics-related information.
+"""
+
+from pathlib import Path
+from typing import Any
+
+from fastapi import APIRouter, Depends, HTTPException, Response
+
+from ....daemon.backend.abstract import Backend
+from ..dependencies import get_backend
+
+router = APIRouter(
+ prefix="/kinematics",
+)
+
+STL_ASSETS_DIR = (
+ Path(__file__).parent.parent.parent.parent
+ / "descriptions"
+ / "reachy_mini"
+ / "urdf"
+ / "assets"
+)
+
+
+@router.get("/info")
+async def get_kinematics_info(
+ backend: Backend = Depends(get_backend),
+) -> dict[str, Any]:
+ """Get the current information of the kinematics."""
+ return {
+ "info": {
+ "engine": backend.kinematics_engine,
+ "collision check": backend.check_collision,
+ }
+ }
+
+
+@router.get("/urdf")
+async def get_urdf(backend: Backend = Depends(get_backend)) -> dict[str, str]:
+ """Get the URDF representation of the robot."""
+ return {"urdf": backend.get_urdf()}
+
+
+@router.get("/stl/{filename}")
+async def get_stl_file(filename: Path) -> Response:
+ """Get the path to an STL asset file."""
+ file_path = STL_ASSETS_DIR / filename
+ try:
+ with open(file_path, "rb") as file:
+ content = file.read()
+ return Response(content, media_type="model/stl")
+ except FileNotFoundError:
+ raise HTTPException(status_code=404, detail=f"STL file not found {file_path}")
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/motors.py b/reachy_mini/src/reachy_mini/daemon/app/routers/motors.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b8b65a115281735f993f7f0f3a2780fb2d39da6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/motors.py
@@ -0,0 +1,41 @@
+"""Motors router.
+
+Provides endpoints to get and set the motor control mode.
+"""
+
+from fastapi import APIRouter, Depends
+from pydantic import BaseModel
+
+from ....daemon.backend.abstract import Backend, MotorControlMode
+from ..dependencies import get_backend
+
+router = APIRouter(
+ prefix="/motors",
+)
+
+
+class MotorStatus(BaseModel):
+ """Represents the status of the motors.
+
+ Exposes
+ - mode: The current motor control mode (enabled, disabled, gravity_compensation).
+ """
+
+ mode: MotorControlMode
+
+
+@router.get("/status")
+async def get_motor_status(backend: Backend = Depends(get_backend)) -> MotorStatus:
+ """Get the current status of the motors."""
+ return MotorStatus(mode=backend.get_motor_control_mode())
+
+
+@router.post("/set_mode/{mode}")
+async def set_motor_mode(
+ mode: MotorControlMode,
+ backend: Backend = Depends(get_backend),
+) -> dict[str, str]:
+ """Set the motor control mode."""
+ backend.set_motor_control_mode(mode)
+
+ return {"status": f"motors changed to {mode} mode"}
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/move.py b/reachy_mini/src/reachy_mini/daemon/app/routers/move.py
new file mode 100644
index 0000000000000000000000000000000000000000..cebea5fc1534363bd6f8ba2696dffbe531d18501
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/move.py
@@ -0,0 +1,261 @@
+"""Movement-related API routes.
+
+This exposes:
+- goto
+- play (wake_up, goto_sleep)
+- stop running moves
+- set_target and streaming set_target
+"""
+
+import asyncio
+import json
+from enum import Enum
+from typing import Any, Coroutine
+from uuid import UUID, uuid4
+
+import numpy as np
+from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect
+from huggingface_hub.errors import RepositoryNotFoundError
+from pydantic import BaseModel
+
+from reachy_mini.motion.recorded_move import RecordedMoves
+
+from ....daemon.backend.abstract import Backend
+from ..dependencies import get_backend, ws_get_backend
+from ..models import AnyPose, FullBodyTarget
+
+move_tasks: dict[UUID, asyncio.Task[None]] = {}
+move_listeners: list[WebSocket] = []
+
+
+router = APIRouter(prefix="/move")
+
+
+class InterpolationMode(str, Enum):
+ """Interpolation modes for movement."""
+
+ # TODO: This should be the same as for the backend
+
+ LINEAR = "linear"
+ MINJERK = "minjerk"
+ EASE = "ease"
+ CARTOON = "cartoon"
+
+
+class GotoModelRequest(BaseModel):
+ """Request model for the goto endpoint."""
+
+ head_pose: AnyPose | None = None
+ antennas: tuple[float, float] | None = None
+ body_yaw: float | None = None
+ duration: float
+ interpolation: InterpolationMode = InterpolationMode.MINJERK
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [
+ {
+ "head_pose": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ "roll": 0.0,
+ "pitch": 0.0,
+ "yaw": 0.0,
+ },
+ "antennas": [0.0, 0.0],
+ "body_yaw": 0.0,
+ "duration": 2.0,
+ "interpolation": "minjerk",
+ },
+ {
+ "antennas": [0.0, 0.0],
+ "duration": 1.0,
+ "interpolation": "linear",
+ },
+ ],
+ }
+ }
+
+
+class MoveUUID(BaseModel):
+ """Model representing a unique identifier for a move task."""
+
+ uuid: UUID
+
+
+def create_move_task(coro: Coroutine[Any, Any, None]) -> MoveUUID:
+ """Create a new move task using async task coroutine."""
+ uuid = uuid4()
+
+ async def notify_listeners(message: str, details: str = "") -> None:
+ for ws in move_listeners:
+ try:
+ await ws.send_json(
+ {
+ "type": message,
+ "uuid": str(uuid),
+ "details": details,
+ }
+ )
+ except (RuntimeError, WebSocketDisconnect):
+ move_listeners.remove(ws)
+
+ async def wrap_coro() -> None:
+ try:
+ await notify_listeners("move_started")
+ await coro
+ await notify_listeners("move_completed")
+ except Exception as e:
+ await notify_listeners("move_failed", details=str(e))
+ except asyncio.CancelledError:
+ await notify_listeners("move_cancelled")
+ finally:
+ move_tasks.pop(uuid, None)
+
+ task = asyncio.create_task(wrap_coro())
+ move_tasks[uuid] = task
+
+ return MoveUUID(uuid=uuid)
+
+
+async def stop_move_task(uuid: UUID) -> dict[str, str]:
+ """Stop a running move task by cancelling it."""
+ if uuid not in move_tasks:
+ raise KeyError(f"Running move with UUID {uuid} not found")
+
+ task = move_tasks.pop(uuid, None)
+ assert task is not None
+
+ if task:
+ if task.cancel():
+ try:
+ await task
+ except asyncio.CancelledError:
+ pass
+
+ return {
+ "message": f"Stopped move with UUID: {uuid}",
+ }
+
+
+@router.get("/running")
+async def get_running_moves() -> list[MoveUUID]:
+ """Get a list of currently running move tasks."""
+ return [MoveUUID(uuid=uuid) for uuid in move_tasks.keys()]
+
+
+@router.post("/goto")
+async def goto(
+ goto_req: GotoModelRequest, backend: Backend = Depends(get_backend)
+) -> MoveUUID:
+ """Request a movement to a specific target."""
+ return create_move_task(
+ backend.goto_target(
+ head=goto_req.head_pose.to_pose_array() if goto_req.head_pose else None,
+ antennas=np.array(goto_req.antennas) if goto_req.antennas else None,
+ body_yaw=goto_req.body_yaw,
+ duration=goto_req.duration,
+ )
+ )
+
+
+@router.post("/play/wake_up")
+async def play_wake_up(backend: Backend = Depends(get_backend)) -> MoveUUID:
+ """Request the robot to wake up."""
+ return create_move_task(backend.wake_up())
+
+
+@router.post("/play/goto_sleep")
+async def play_goto_sleep(backend: Backend = Depends(get_backend)) -> MoveUUID:
+ """Request the robot to go to sleep."""
+ return create_move_task(backend.goto_sleep())
+
+
+@router.get("/recorded-move-datasets/list/{dataset_name:path}")
+async def list_recorded_move_dataset(
+ dataset_name: str,
+) -> list[str]:
+ """List available recorded moves in a dataset."""
+ try:
+ moves = RecordedMoves(dataset_name)
+ except RepositoryNotFoundError as e:
+ raise HTTPException(status_code=404, detail=str(e))
+
+ return moves.list_moves()
+
+
+@router.post("/play/recorded-move-dataset/{dataset_name:path}/{move_name}")
+async def play_recorded_move_dataset(
+ dataset_name: str,
+ move_name: str,
+ backend: Backend = Depends(get_backend),
+) -> MoveUUID:
+ """Request the robot to play a predefined recorded move from a dataset."""
+ try:
+ recorded_moves = RecordedMoves(dataset_name)
+ except RepositoryNotFoundError as e:
+ raise HTTPException(status_code=404, detail=str(e))
+ try:
+ move = recorded_moves.get(move_name)
+ except ValueError as e:
+ raise HTTPException(status_code=404, detail=str(e))
+ return create_move_task(backend.play_move(move))
+
+
+@router.post("/stop")
+async def stop_move(uuid: MoveUUID) -> dict[str, str]:
+ """Stop a running move task."""
+ return await stop_move_task(uuid.uuid)
+
+
+@router.websocket("/ws/updates")
+async def ws_move_updates(
+ websocket: WebSocket,
+) -> None:
+ """WebSocket route to stream move updates."""
+ await websocket.accept()
+ try:
+ move_listeners.append(websocket)
+ while True:
+ _ = await websocket.receive_text()
+ except WebSocketDisconnect:
+ move_listeners.remove(websocket)
+
+
+# --- FullBodyTarget streaming and single set_target ---
+@router.post("/set_target")
+async def set_target(
+ target: FullBodyTarget,
+ backend: Backend = Depends(get_backend),
+) -> dict[str, str]:
+ """POST route to set a single FullBodyTarget."""
+ backend.set_target(
+ head=target.target_head_pose.to_pose_array()
+ if target.target_head_pose
+ else None,
+ antennas=np.array(target.target_antennas) if target.target_antennas else None,
+ body_yaw=target.target_body_yaw,
+ )
+ return {"status": "ok"}
+
+
+@router.websocket("/ws/set_target")
+async def ws_set_target(
+ websocket: WebSocket, backend: Backend = Depends(ws_get_backend)
+) -> None:
+ """WebSocket route to stream FullBodyTarget set_target calls."""
+ await websocket.accept()
+ try:
+ while True:
+ data = await websocket.receive_text()
+ try:
+ target = FullBodyTarget.model_validate_json(data)
+ await set_target(target, backend)
+
+ except Exception as e:
+ await websocket.send_text(
+ json.dumps({"status": "error", "detail": str(e)})
+ )
+ except WebSocketDisconnect:
+ pass
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/state.py b/reachy_mini/src/reachy_mini/daemon/app/routers/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ba1aea21a545babf68cf870c6f9d9f2324bc822
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/state.py
@@ -0,0 +1,146 @@
+"""State-related API routes.
+
+This exposes:
+- basic get routes to retrieve most common fields
+- full state and streaming state updates
+"""
+
+import asyncio
+from datetime import datetime, timezone
+from typing import Any
+
+from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
+
+from ....daemon.backend.abstract import Backend
+from ..dependencies import get_backend, ws_get_backend
+from ..models import AnyPose, FullState, as_any_pose
+
+router = APIRouter(prefix="/state")
+
+
+@router.get("/present_head_pose")
+async def get_head_pose(
+ use_pose_matrix: bool = False,
+ backend: Backend = Depends(get_backend),
+) -> AnyPose:
+ """Get the present head pose.
+
+ Arguments:
+ use_pose_matrix (bool): Whether to use the pose matrix representation (4x4 flattened) or the translation + Euler angles representation (x, y, z, roll, pitch, yaw).
+ backend (Backend): The backend instance.
+
+ Returns:
+ AnyPose: The present head pose.
+
+ """
+ return as_any_pose(backend.get_present_head_pose(), use_pose_matrix)
+
+
+@router.get("/present_body_yaw")
+async def get_body_yaw(
+ backend: Backend = Depends(get_backend),
+) -> float:
+ """Get the present body yaw (in radians)."""
+ return backend.get_present_body_yaw()
+
+
+@router.get("/present_antenna_joint_positions")
+async def get_antenna_joint_positions(
+ backend: Backend = Depends(get_backend),
+) -> tuple[float, float]:
+ """Get the present antenna joint positions (in radians) - (left, right)."""
+ pos = backend.get_present_antenna_joint_positions()
+ assert len(pos) == 2
+ return (pos[0], pos[1])
+
+
+@router.get("/full")
+async def get_full_state(
+ with_control_mode: bool = True,
+ with_head_pose: bool = True,
+ with_target_head_pose: bool = False,
+ with_head_joints: bool = False,
+ with_target_head_joints: bool = False,
+ with_body_yaw: bool = True,
+ with_target_body_yaw: bool = False,
+ with_antenna_positions: bool = True,
+ with_target_antenna_positions: bool = False,
+ with_passive_joints: bool = False,
+ use_pose_matrix: bool = False,
+ backend: Backend = Depends(get_backend),
+) -> FullState:
+ """Get the full robot state, with optional fields."""
+ result: dict[str, Any] = {}
+
+ if with_control_mode:
+ result["control_mode"] = backend.get_motor_control_mode().value
+
+ if with_head_pose:
+ pose = backend.get_present_head_pose()
+ result["head_pose"] = as_any_pose(pose, use_pose_matrix)
+ if with_target_head_pose:
+ target_pose = backend.target_head_pose
+ assert target_pose is not None
+ result["target_head_pose"] = as_any_pose(target_pose, use_pose_matrix)
+ if with_head_joints:
+ result["head_joints"] = backend.get_present_head_joint_positions()
+ if with_target_head_joints:
+ result["target_head_joints"] = backend.target_head_joint_positions
+ if with_body_yaw:
+ result["body_yaw"] = backend.get_present_body_yaw()
+ if with_target_body_yaw:
+ result["target_body_yaw"] = backend.target_body_yaw
+ if with_antenna_positions:
+ result["antennas_position"] = backend.get_present_antenna_joint_positions()
+ if with_target_antenna_positions:
+ result["target_antennas_position"] = backend.target_antenna_joint_positions
+ if with_passive_joints:
+ joints = backend.get_present_passive_joint_positions()
+ if joints is not None:
+ result["passive_joints"] = list(joints.values())
+ else:
+ result["passive_joints"] = None
+
+ result["timestamp"] = datetime.now(timezone.utc)
+ return FullState.model_validate(result)
+
+
+@router.websocket("/ws/full")
+async def ws_full_state(
+ websocket: WebSocket,
+ frequency: float = 10.0,
+ with_head_pose: bool = True,
+ with_target_head_pose: bool = False,
+ with_head_joints: bool = False,
+ with_target_head_joints: bool = False,
+ with_body_yaw: bool = True,
+ with_target_body_yaw: bool = False,
+ with_antenna_positions: bool = True,
+ with_target_antenna_positions: bool = False,
+ with_passive_joints: bool = False,
+ use_pose_matrix: bool = False,
+ backend: Backend = Depends(ws_get_backend),
+) -> None:
+ """WebSocket endpoint to stream the full state of the robot."""
+ await websocket.accept()
+ period = 1.0 / frequency
+
+ try:
+ while True:
+ full_state = await get_full_state(
+ with_head_pose=with_head_pose,
+ with_target_head_pose=with_target_head_pose,
+ with_head_joints=with_head_joints,
+ with_target_head_joints=with_target_head_joints,
+ with_body_yaw=with_body_yaw,
+ with_target_body_yaw=with_target_body_yaw,
+ with_antenna_positions=with_antenna_positions,
+ with_target_antenna_positions=with_target_antenna_positions,
+ with_passive_joints=with_passive_joints,
+ use_pose_matrix=use_pose_matrix,
+ backend=backend,
+ )
+ await websocket.send_text(full_state.model_dump_json())
+ await asyncio.sleep(period)
+ except WebSocketDisconnect:
+ pass
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/update.py b/reachy_mini/src/reachy_mini/daemon/app/routers/update.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd2e4bf8f43dd0e8b49e2b8f398634bc92a1a880
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/update.py
@@ -0,0 +1,86 @@
+"""Update router for Reachy Mini Daemon API.
+
+This module provides endpoints to check for updates, start updates, and monitor update status.
+"""
+
+import logging
+import threading
+
+import requests
+from fastapi import APIRouter, HTTPException, WebSocket
+
+from reachy_mini.daemon.app import bg_job_register
+from reachy_mini.daemon.app.bg_job_register import JobInfo
+from reachy_mini.utils.wireless_version.update import update_reachy_mini
+from reachy_mini.utils.wireless_version.update_available import (
+ get_local_version,
+ get_pypi_version,
+ is_update_available,
+)
+
+router = APIRouter(prefix="/update")
+busy_lock = threading.Lock()
+
+
+@router.get("/available")
+def available(pre_release: bool = False) -> dict[str, dict[str, dict[str, bool | str]]]:
+ """Check if an update is available for Reachy Mini Wireless."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=400, detail="Update is in progress")
+
+ current_version = str(get_local_version("reachy_mini"))
+
+ try:
+ is_available = is_update_available("reachy_mini", pre_release)
+ available = str(get_pypi_version("reachy_mini", pre_release))
+ except (ConnectionError, requests.exceptions.ConnectionError):
+ is_available = False
+ available = "unknown"
+
+ return {
+ "update": {
+ "reachy_mini": {
+ "is_available": is_available,
+ "current_version": current_version,
+ "available_version": available,
+ }
+ }
+ }
+
+
+@router.post("/start")
+def start_update(pre_release: bool = False) -> dict[str, str]:
+ """Start the update process for Reachy Mini Wireless version."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=400, detail="Update already in progress")
+
+ if not is_update_available("reachy_mini", pre_release):
+ raise HTTPException(status_code=400, detail="No update available")
+
+ async def update_wrapper(logger: logging.Logger) -> None:
+ with busy_lock:
+ await update_reachy_mini(pre_release, logger)
+
+ job_uuid = bg_job_register.run_command(
+ "update_reachy_mini",
+ update_wrapper,
+ )
+
+ return {"job_id": job_uuid}
+
+
+@router.get("/info")
+def get_update_info(job_id: str) -> JobInfo:
+ """Get the info of an update job."""
+ try:
+ return bg_job_register.get_info(job_id)
+ except ValueError as e:
+ raise HTTPException(status_code=404, detail=str(e))
+
+
+@router.websocket("/ws/logs")
+async def websocket_logs(websocket: WebSocket, job_id: str) -> None:
+ """WebSocket endpoint to stream update logs in real time."""
+ await websocket.accept()
+ await bg_job_register.ws_poll_info(websocket, job_id)
+ await websocket.close()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/volume.py b/reachy_mini/src/reachy_mini/daemon/app/routers/volume.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5099018a39fa13e8a521922d259138786627fc2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/volume.py
@@ -0,0 +1,416 @@
+"""Volume control API routes.
+
+This exposes:
+- get current volume
+- set volume
+- same for microphone
+- play test sound (optional)
+"""
+
+import logging
+import platform
+import subprocess
+from typing import Optional
+
+from fastapi import APIRouter, Depends, HTTPException
+from pydantic import BaseModel, Field
+
+from reachy_mini.daemon.app.dependencies import get_backend
+from reachy_mini.daemon.backend.abstract import Backend
+
+router = APIRouter(prefix="/volume")
+logger = logging.getLogger(__name__)
+
+# Constants
+AUDIO_COMMAND_TIMEOUT = 2 # Timeout in seconds for audio commands
+
+# Device-specific card names for amixer
+DEVICE_CARD_NAMES = {
+ "reachy_mini_audio": "Audio", # Reachy Mini Audio device
+ "respeaker": "Array", # Legacy ReSpeaker device
+ "default": "Audio", # Default to Reachy Mini Audio
+}
+
+
+class VolumeRequest(BaseModel):
+ """Request model for setting volume."""
+
+ volume: int = Field(..., ge=0, le=100, description="Volume level (0-100)")
+
+
+class VolumeResponse(BaseModel):
+ """Response model for volume operations."""
+
+ volume: int
+ device: str
+ platform: str
+
+
+class TestSoundResponse(BaseModel):
+ """Response model for test sound operations."""
+
+ status: str
+ message: str
+
+
+def get_current_platform() -> str:
+ """Get the current platform."""
+ system = platform.system()
+ if system == "Darwin":
+ return "macOS"
+ elif system == "Linux":
+ return "Linux"
+ else:
+ return system
+
+
+def detect_audio_device() -> str:
+ """Detect the current audio output device."""
+ system = platform.system()
+
+ if system == "Linux":
+ # Try to detect if Reachy Mini Audio or legacy Respeaker is available
+ try:
+ result = subprocess.run(
+ ["aplay", "-l"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ )
+ output_lower = result.stdout.lower()
+ if "reachy mini audio" in output_lower:
+ return "reachy_mini_audio"
+ elif "respeaker" in output_lower:
+ return "respeaker"
+ except (subprocess.TimeoutExpired, FileNotFoundError):
+ pass
+ return "default"
+ elif system == "Darwin":
+ return "system"
+ else:
+ return "unknown"
+
+
+def get_linux_card_name() -> str:
+ """Get the appropriate card name for Linux amixer commands based on detected device."""
+ device = detect_audio_device()
+ return DEVICE_CARD_NAMES.get(device, DEVICE_CARD_NAMES["default"])
+
+
+# macOS Volume Control
+
+
+def get_volume_macos() -> Optional[int]:
+ """Get current system volume on macOS."""
+ try:
+ result = subprocess.run(
+ ["osascript", "-e", "output volume of (get volume settings)"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ )
+ if result.returncode == 0:
+ return int(result.stdout.strip())
+ except (subprocess.TimeoutExpired, FileNotFoundError, ValueError) as e:
+ logger.error(f"Failed to get macOS volume: {e}")
+ return None
+
+
+def set_volume_macos(volume: int) -> bool:
+ """Set system volume on macOS using osascript."""
+ try:
+ subprocess.run(
+ ["osascript", "-e", f"set volume output volume {volume}"],
+ capture_output=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ check=True,
+ )
+ return True
+ except (
+ subprocess.TimeoutExpired,
+ FileNotFoundError,
+ subprocess.CalledProcessError,
+ ) as e:
+ logger.error(f"Failed to set macOS volume: {e}")
+ return False
+
+
+# Linux Volume Control
+
+
+def get_volume_linux() -> Optional[int]:
+ """Get current volume on Linux using amixer."""
+ card_name = get_linux_card_name()
+ try:
+ result = subprocess.run(
+ ["amixer", "-c", card_name, "sget", "PCM"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ )
+ if result.returncode == 0:
+ # Parse output to extract volume percentage
+ for line in result.stdout.splitlines():
+ if "Left:" in line and "[" in line:
+ # Extract percentage between brackets
+ parts = line.split("[")
+ for part in parts:
+ if "%" in part:
+ volume_str = part.split("%")[0]
+ return int(volume_str)
+ except (subprocess.TimeoutExpired, FileNotFoundError, ValueError) as e:
+ logger.error(f"Failed to get Linux volume: {e}")
+ return None
+
+
+def set_volume_linux(volume: int) -> bool:
+ """Set current volume on Linux using amixer."""
+ card_name = get_linux_card_name()
+ try:
+ subprocess.run(
+ ["amixer", "-c", card_name, "sset", "PCM", f"{volume}%"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ check=True,
+ )
+ return True
+ except (
+ subprocess.TimeoutExpired,
+ FileNotFoundError,
+ subprocess.CalledProcessError,
+ ValueError,
+ ) as e:
+ logger.error(f"Failed to set Linux volume: {e}")
+ return False
+
+
+# API Endpoints - Speaker Volume
+
+
+@router.get("/current")
+async def get_volume() -> VolumeResponse:
+ """Get the current volume level."""
+ system = get_current_platform()
+ device = detect_audio_device()
+
+ volume = None
+ if system == "macOS":
+ volume = get_volume_macos()
+ elif system == "Linux":
+ volume = get_volume_linux()
+
+ if volume is None:
+ raise HTTPException(status_code=500, detail="Failed to get volume")
+
+ return VolumeResponse(volume=volume, device=device, platform=system)
+
+
+@router.post("/set")
+async def set_volume(
+ volume_req: VolumeRequest,
+ backend: Backend = Depends(get_backend),
+) -> VolumeResponse:
+ """Set the volume level and play a test sound."""
+ system = get_current_platform()
+ device = detect_audio_device()
+
+ success = False
+ if system == "macOS":
+ success = set_volume_macos(volume_req.volume)
+ elif system == "Linux":
+ success = set_volume_linux(volume_req.volume)
+ else:
+ raise HTTPException(
+ status_code=501,
+ detail=f"Volume control not supported on {system}",
+ )
+
+ if not success:
+ raise HTTPException(status_code=500, detail="Failed to set volume")
+
+ # Play test sound
+ test_sound = "impatient1.wav"
+ if backend.audio:
+ try:
+ backend.audio.play_sound(test_sound, autoclean=True)
+ except Exception as e:
+ msg = str(e).lower()
+ if "device unavailable" in msg or "-9985" in msg:
+ logger.warning(
+ "Test sound not played: audio device busy (likely GStreamer): %s",
+ e,
+ )
+ else:
+ logger.warning("Failed to play test sound: %s", e)
+ else:
+ logger.warning("No audio backend available, skipping test sound.")
+
+ return VolumeResponse(volume=volume_req.volume, device=device, platform=system)
+
+
+@router.post("/test-sound")
+async def play_test_sound(backend: Backend = Depends(get_backend)) -> TestSoundResponse:
+ """Play a test sound."""
+ test_sound = "impatient1.wav"
+
+ if not backend.audio:
+ raise HTTPException(status_code=503, detail="Audio device not available")
+
+ try:
+ backend.audio.play_sound(test_sound, autoclean=True)
+ return TestSoundResponse(status="ok", message="Test sound played")
+ except Exception as e:
+ msg = str(e).lower()
+
+ # Special-case ALSA / PortAudio device-busy situation
+ if "device unavailable" in msg or "-9985" in msg:
+ logger.warning(
+ "Test sound request while audio device is busy (likely GStreamer): %s",
+ e,
+ )
+ # Still 200, but tell the caller it was skipped
+ return TestSoundResponse(
+ status="busy",
+ message="Audio device is currently in use, test sound was skipped.",
+ )
+
+ # Any other error is treated as a real failure
+ logger.error("Failed to play test sound: %s", e, exc_info=True)
+ raise HTTPException(
+ status_code=500,
+ detail="Failed to play test sound (see logs for details)",
+ )
+
+
+# macOS Microphone Control
+
+
+def get_microphone_volume_macos() -> Optional[int]:
+ """Get current microphone input volume on macOS."""
+ try:
+ result = subprocess.run(
+ ["osascript", "-e", "input volume of (get volume settings)"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ )
+ if result.returncode == 0:
+ return int(result.stdout.strip())
+ except (subprocess.TimeoutExpired, FileNotFoundError, ValueError) as e:
+ logger.error(f"Failed to get macOS microphone volume: {e}")
+ return None
+
+
+def set_microphone_volume_macos(volume: int) -> bool:
+ """Set microphone input volume on macOS using osascript."""
+ try:
+ subprocess.run(
+ ["osascript", "-e", f"set volume input volume {volume}"],
+ capture_output=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ check=True,
+ )
+ return True
+ except (
+ subprocess.TimeoutExpired,
+ FileNotFoundError,
+ subprocess.CalledProcessError,
+ ) as e:
+ logger.error(f"Failed to set macOS microphone volume: {e}")
+ return False
+
+
+# Linux Microphone Control
+
+
+def get_microphone_volume_linux() -> Optional[int]:
+ """Get current microphone input volume on Linux using amixer."""
+ card_name = get_linux_card_name()
+ try:
+ result = subprocess.run(
+ ["amixer", "-c", card_name, "sget", "Headset"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ )
+ if result.returncode == 0:
+ # Parse output to extract volume percentage
+ for line in result.stdout.splitlines():
+ if "Left:" in line and "[" in line:
+ parts = line.split("[")
+ for part in parts:
+ if "%" in part:
+ volume_str = part.split("%")[0]
+ return int(volume_str)
+ except (subprocess.TimeoutExpired, FileNotFoundError, ValueError) as e:
+ logger.error(f"Failed to get Linux microphone volume: {e}")
+ return None
+
+
+def set_microphone_volume_linux(volume: int) -> bool:
+ """Set microphone input volume on Linux using amixer."""
+ card_name = get_linux_card_name()
+ try:
+ subprocess.run(
+ ["amixer", "-c", card_name, "sset", "Headset", f"{volume}%"],
+ capture_output=True,
+ text=True,
+ timeout=AUDIO_COMMAND_TIMEOUT,
+ check=True,
+ )
+ return True
+ except (
+ subprocess.TimeoutExpired,
+ FileNotFoundError,
+ subprocess.CalledProcessError,
+ ValueError,
+ ) as e:
+ logger.error(f"Failed to set Linux microphone volume: {e}")
+ return False
+
+
+# API Endpoints - Microphone Volume
+
+
+@router.get("/microphone/current")
+async def get_microphone_volume() -> VolumeResponse:
+ """Get the current microphone input volume level."""
+ system = get_current_platform()
+ device = detect_audio_device()
+
+ volume = None
+ if system == "macOS":
+ volume = get_microphone_volume_macos()
+ elif system == "Linux":
+ volume = get_microphone_volume_linux()
+
+ if volume is None:
+ raise HTTPException(status_code=500, detail="Failed to get microphone volume")
+
+ return VolumeResponse(volume=volume, device=device, platform=system)
+
+
+@router.post("/microphone/set")
+async def set_microphone_volume(
+ volume_req: VolumeRequest,
+) -> VolumeResponse:
+ """Set the microphone input volume level."""
+ system = get_current_platform()
+ device = detect_audio_device()
+
+ success = False
+ if system == "macOS":
+ success = set_microphone_volume_macos(volume_req.volume)
+ elif system == "Linux":
+ success = set_microphone_volume_linux(volume_req.volume)
+ else:
+ raise HTTPException(
+ status_code=501,
+ detail=f"Microphone volume control not supported on {system}",
+ )
+
+ if not success:
+ raise HTTPException(status_code=500, detail="Failed to set microphone volume")
+
+ return VolumeResponse(volume=volume_req.volume, device=device, platform=system)
diff --git a/reachy_mini/src/reachy_mini/daemon/app/routers/wifi_config.py b/reachy_mini/src/reachy_mini/daemon/app/routers/wifi_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..0285ecd3465c22a00ca86840f0180cf4768b89ff
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/routers/wifi_config.py
@@ -0,0 +1,219 @@
+"""WiFi Configuration Routers."""
+
+import logging
+from enum import Enum
+from threading import Lock, Thread
+
+import nmcli
+from fastapi import APIRouter, HTTPException
+from pydantic import BaseModel
+
+HOTSPOT_SSID = "reachy-mini-ap"
+HOTSPOT_PASSWORD = "reachy-mini"
+
+
+router = APIRouter(
+ prefix="/wifi",
+)
+
+busy_lock = Lock()
+error: Exception | None = None
+logger = logging.getLogger(__name__)
+
+
+class WifiMode(Enum):
+ """WiFi possible modes."""
+
+ HOTSPOT = "hotspot"
+ WLAN = "wlan"
+ DISCONNECTED = "disconnected"
+ BUSY = "busy"
+
+
+class WifiStatus(BaseModel):
+ """WiFi status model."""
+
+ mode: WifiMode
+ known_networks: list[str]
+ connected_network: str | None
+
+
+def get_current_wifi_mode() -> WifiMode:
+ """Get the current WiFi mode."""
+ if busy_lock.locked():
+ return WifiMode.BUSY
+
+ conn = get_wifi_connections()
+ if check_if_connection_active("Hotspot"):
+ return WifiMode.HOTSPOT
+ elif any(c.device != "--" for c in conn):
+ return WifiMode.WLAN
+ else:
+ return WifiMode.DISCONNECTED
+
+
+@router.get("/status")
+def get_wifi_status() -> WifiStatus:
+ """Get the current WiFi status."""
+ mode = get_current_wifi_mode()
+
+ connections = get_wifi_connections()
+ known_networks = [c.name for c in connections if c.name != "Hotspot"]
+
+ connected_network = next((c.name for c in connections if c.device != "--"), None)
+
+ return WifiStatus(
+ mode=mode,
+ known_networks=known_networks,
+ connected_network=connected_network,
+ )
+
+
+@router.get("/error")
+def get_last_wifi_error() -> dict[str, str | None]:
+ """Get the last WiFi error."""
+ global error
+ if error is None:
+ return {"error": None}
+ return {"error": str(error)}
+
+
+@router.post("/reset_error")
+def reset_last_wifi_error() -> dict[str, str]:
+ """Reset the last WiFi error."""
+ global error
+ error = None
+ return {"status": "ok"}
+
+
+@router.post("/setup_hotspot")
+def setup_hotspot(
+ ssid: str = HOTSPOT_SSID,
+ password: str = HOTSPOT_PASSWORD,
+) -> None:
+ """Set up a WiFi hotspot. It will create a new hotspot using nmcli if one does not already exist."""
+ if busy_lock.locked():
+ raise HTTPException(status_code=409, detail="Another operation is in progress.")
+
+ def hotspot() -> None:
+ with busy_lock:
+ setup_wifi_connection(
+ name="Hotspot", ssid=ssid, password=password, is_hotspot=True
+ )
+
+ Thread(target=hotspot).start()
+ # TODO: wait for it to be really started
+
+
+@router.post("/connect")
+def connect_to_wifi_network(
+ ssid: str,
+ password: str,
+) -> None:
+ """Connect to a WiFi network. It will create a new connection using nmcli if the specified SSID is not already configured."""
+ logger.warning(f"Request to connect to WiFi network '{ssid}' received.")
+
+ if busy_lock.locked():
+ raise HTTPException(status_code=409, detail="Another operation is in progress.")
+
+ def connect() -> None:
+ global error
+ with busy_lock:
+ try:
+ error = None
+ setup_wifi_connection(name=ssid, ssid=ssid, password=password)
+ except Exception as e:
+ error = e
+ logger.error(f"Failed to connect to WiFi network '{ssid}': {e}")
+ logger.info("Reverting to hotspot...")
+ remove_connection(name=ssid)
+ setup_wifi_connection(
+ name="Hotspot",
+ ssid=HOTSPOT_SSID,
+ password=HOTSPOT_PASSWORD,
+ is_hotspot=True,
+ )
+
+ Thread(target=connect).start()
+ # TODO: wait for it to be really connected
+
+
+@router.post("/scan_and_list")
+def scan_wifi() -> list[str]:
+ """Scan for available WiFi networks ordered by signal power."""
+ wifi = scan_available_wifi()
+
+ seen = set()
+ ssids = [x.ssid for x in wifi if x.ssid not in seen and not seen.add(x.ssid)] # type: ignore
+
+ return ssids
+
+
+# NMCLI WRAPPERS
+def scan_available_wifi() -> list[nmcli.data.device.DeviceWifi]:
+ """Scan for available WiFi networks."""
+ nmcli.device.wifi_rescan()
+ devices: list[nmcli.data.device.DeviceWifi] = nmcli.device.wifi()
+ return devices
+
+
+def get_wifi_connections() -> list[nmcli.data.connection.Connection]:
+ """Get the list of WiFi connection."""
+ return [conn for conn in nmcli.connection() if conn.conn_type == "wifi"]
+
+
+def check_if_connection_exists(name: str) -> bool:
+ """Check if a WiFi connection with the given SSID already exists."""
+ return any(c.name == name for c in get_wifi_connections())
+
+
+def check_if_connection_active(name: str) -> bool:
+ """Check if a WiFi connection with the given SSID is currently active."""
+ return any(c.name == name and c.device != "--" for c in get_wifi_connections())
+
+
+def setup_wifi_connection(
+ name: str, ssid: str, password: str, is_hotspot: bool = False
+) -> None:
+ """Set up a WiFi connection using nmcli."""
+ logger.info(f"Setting up WiFi connection (ssid='{ssid}')...")
+
+ if not check_if_connection_exists(name):
+ logger.info("WiFi configuration does not exist. Creating...")
+ if is_hotspot:
+ nmcli.device.wifi_hotspot(ssid=ssid, password=password)
+ else:
+ nmcli.device.wifi_connect(ssid=ssid, password=password)
+ return
+
+ logger.info("WiFi configuration already exists.")
+ if not check_if_connection_active(name):
+ logger.info("WiFi is not active. Activating...")
+ nmcli.connection.up(name)
+ return
+
+ logger.info(f"Connection {name} is already active.")
+
+
+def remove_connection(name: str) -> None:
+ """Remove a WiFi connection using nmcli."""
+ if check_if_connection_exists(name):
+ logger.info(f"Removing WiFi connection '{name}'...")
+ nmcli.connection.delete(name)
+
+
+# Setup WiFi connection on startup
+
+# This make sure the wlan0 is up and running
+scan_available_wifi()
+
+# On startup, if no WiFi connection is active, set up the default hotspot
+if get_current_wifi_mode() == WifiMode.DISCONNECTED:
+ logger.info("No WiFi connection active. Setting up hotspot...")
+
+ setup_wifi_connection(
+ name="Hotspot",
+ ssid=HOTSPOT_SSID,
+ password=HOTSPOT_PASSWORD,
+ is_hotspot=True,
+ )
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/__init__.py b/reachy_mini/src/reachy_mini/daemon/app/services/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7775ec9a514eec1cf24d26c0f68f3bea568da78
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/__init__.py
@@ -0,0 +1 @@
+"""Services package."""
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py
new file mode 100644
index 0000000000000000000000000000000000000000..15dc4399e353b7eff49c5dac9e95283fa0a12ac3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py
@@ -0,0 +1,454 @@
+#!/usr/bin/env python3
+"""Bluetooth service for Reachy Mini using direct DBus API.
+
+Includes a fixed NoInputNoOutput agent for automatic Just Works pairing.
+"""
+# mypy: ignore-errors
+
+import logging
+import os
+import subprocess
+from typing import Callable
+
+import dbus
+import dbus.mainloop.glib
+import dbus.service
+from gi.repository import GLib
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+# Service and Characteristic UUIDs
+SERVICE_UUID = "12345678-1234-5678-1234-56789abcdef0"
+COMMAND_CHAR_UUID = "12345678-1234-5678-1234-56789abcdef1"
+RESPONSE_CHAR_UUID = "12345678-1234-5678-1234-56789abcdef2"
+
+BLUEZ_SERVICE_NAME = "org.bluez"
+GATT_MANAGER_IFACE = "org.bluez.GattManager1"
+DBUS_OM_IFACE = "org.freedesktop.DBus.ObjectManager"
+DBUS_PROP_IFACE = "org.freedesktop.DBus.Properties"
+GATT_SERVICE_IFACE = "org.bluez.GattService1"
+GATT_CHRC_IFACE = "org.bluez.GattCharacteristic1"
+LE_ADVERTISING_MANAGER_IFACE = "org.bluez.LEAdvertisingManager1"
+LE_ADVERTISEMENT_IFACE = "org.bluez.LEAdvertisement1"
+AGENT_PATH = "/org/bluez/agent"
+
+
+# =======================
+# BLE Agent for Just Works
+# =======================
+class NoInputAgent(dbus.service.Object):
+ """BLE Agent for Just Works pairing."""
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def Release(self, *args):
+ """Handle release of the agent."""
+ logger.info("Agent released")
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="s")
+ def RequestPinCode(self, *args):
+ """Automatically provide an empty pin code for Just Works pairing."""
+ logger.info(f"RequestPinCode called with args: {args}, returning empty")
+ return ""
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="u")
+ def RequestPasskey(self, *args):
+ """Automatically provide a passkey of 0 for Just Works pairing."""
+ logger.info(f"RequestPasskey called with args: {args}, returning 0")
+ return dbus.UInt32(0)
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def RequestConfirmation(self, *args):
+ """Automatically confirm the pairing request."""
+ logger.info(
+ f"RequestConfirmation called with args: {args}, accepting automatically"
+ )
+ return
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def DisplayPinCode(self, *args):
+ """Handle displaying the pin code (not used in Just Works)."""
+ logger.info(f"DisplayPinCode called with args: {args}")
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def DisplayPasskey(self, *args):
+ """Handle displaying the passkey (not used in Just Works)."""
+ logger.info(f"DisplayPasskey called with args: {args}")
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def AuthorizeService(self, *args):
+ """Handle service authorization requests."""
+ logger.info(f"AuthorizeService called with args: {args}")
+
+ @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
+ def Cancel(self, *args):
+ """Handle cancellation of the agent request."""
+ logger.info("Agent request canceled")
+
+
+# =======================
+# BLE Advertisement
+# =======================
+class Advertisement(dbus.service.Object):
+ """BLE Advertisement."""
+
+ PATH_BASE = "/org/bluez/advertisement"
+
+ def __init__(self, bus, index, advertising_type, local_name):
+ """Initialize the Advertisement."""
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.ad_type = advertising_type
+ self.local_name = local_name
+ self.service_uuids = None
+ self.include_tx_power = False
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ """Return the properties of the advertisement."""
+ props = {"Type": self.ad_type}
+ if self.local_name:
+ props["LocalName"] = dbus.String(self.local_name)
+ if self.service_uuids:
+ props["ServiceUUIDs"] = dbus.Array(self.service_uuids, signature="s")
+ props["Appearance"] = dbus.UInt16(0x0000)
+ props["Duration"] = dbus.UInt16(0)
+ props["Timeout"] = dbus.UInt16(0)
+ return {LE_ADVERTISEMENT_IFACE: props}
+
+ def get_path(self):
+ """Return the object path."""
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
+ def GetAll(self, interface):
+ """Return all properties of the advertisement."""
+ if interface != LE_ADVERTISEMENT_IFACE:
+ raise dbus.exceptions.DBusException(
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ "Unknown interface " + interface,
+ )
+ return self.get_properties()[LE_ADVERTISEMENT_IFACE]
+
+ @dbus.service.method(LE_ADVERTISEMENT_IFACE, in_signature="", out_signature="")
+ def Release(self):
+ """Handle release of the advertisement."""
+ logger.info("Advertisement released")
+
+
+# =======================
+# BLE Characteristics & Service
+# =======================
+class Characteristic(dbus.service.Object):
+ """GATT Characteristic."""
+
+ def __init__(self, bus, index, uuid, flags, service):
+ """Initialize the Characteristic."""
+ self.path = service.path + "/char" + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.service = service
+ self.flags = flags
+ self.value = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ """Return the properties of the characteristic."""
+ return {
+ GATT_CHRC_IFACE: {
+ "Service": self.service.get_path(),
+ "UUID": self.uuid,
+ "Flags": self.flags,
+ }
+ }
+
+ def get_path(self):
+ """Return the object path."""
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
+ def GetAll(self, interface):
+ """Return all properties of the characteristic."""
+ if interface != GATT_CHRC_IFACE:
+ raise dbus.exceptions.DBusException(
+ "org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
+ )
+ return self.get_properties()[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature="a{sv}", out_signature="ay")
+ def ReadValue(self, options):
+ """Handle read from the characteristic."""
+ return dbus.Array(self.value, signature="y")
+
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature="aya{sv}")
+ def WriteValue(self, value, options):
+ """Handle write to the characteristic."""
+ self.value = value
+
+
+class CommandCharacteristic(Characteristic):
+ """Command Characteristic."""
+
+ def __init__(self, bus, index, service, command_handler: Callable[[bytes], str]):
+ """Initialize the Command Characteristic."""
+ super().__init__(bus, index, COMMAND_CHAR_UUID, ["write"], service)
+ self.command_handler = command_handler
+
+ def WriteValue(self, value, options):
+ """Handle write to the Command Characteristic."""
+ command_bytes = bytes(value)
+ response = self.command_handler(command_bytes)
+ self.service.response_char.value = [
+ dbus.Byte(b) for b in response.encode("utf-8")
+ ]
+ logger.info(f"Command received: {response}")
+
+
+class ResponseCharacteristic(Characteristic):
+ """Response Characteristic.""" ""
+
+ def __init__(self, bus, index, service):
+ """Initialize the Response Characteristic."""
+ super().__init__(bus, index, RESPONSE_CHAR_UUID, ["read", "notify"], service)
+
+
+class Service(dbus.service.Object):
+ """GATT Service."""
+
+ PATH_BASE = "/org/bluez/service"
+
+ def __init__(
+ self, bus, index, uuid, primary, command_handler: Callable[[bytes], str]
+ ):
+ """Initialize the GATT Service."""
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.primary = primary
+ self.characteristics = []
+ dbus.service.Object.__init__(self, bus, self.path)
+ # Response characteristic first
+ self.response_char = ResponseCharacteristic(bus, 1, self)
+ self.add_characteristic(self.response_char)
+ # Command characteristic
+ self.add_characteristic(CommandCharacteristic(bus, 0, self, command_handler))
+
+ def get_properties(self):
+ """Return the properties of the service."""
+ return {
+ GATT_SERVICE_IFACE: {
+ "UUID": self.uuid,
+ "Primary": self.primary,
+ "Characteristics": [ch.get_path() for ch in self.characteristics],
+ }
+ }
+
+ def get_path(self):
+ """Return the object path."""
+ return dbus.ObjectPath(self.path)
+
+ def add_characteristic(self, ch):
+ """Add a characteristic to the service."""
+ self.characteristics.append(ch)
+
+ @dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
+ def GetAll(self, interface):
+ """Return all properties of the service."""
+ if interface != GATT_SERVICE_IFACE:
+ raise dbus.exceptions.DBusException(
+ "org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
+ )
+ return self.get_properties()[GATT_SERVICE_IFACE]
+
+
+class Application(dbus.service.Object):
+ """GATT Application."""
+
+ def __init__(self, bus, command_handler: Callable[[bytes], str]):
+ """Initialize the GATT Application."""
+ self.path = "/"
+ self.services = []
+ dbus.service.Object.__init__(self, bus, self.path)
+ self.services.append(Service(bus, 0, SERVICE_UUID, True, command_handler))
+
+ def get_path(self):
+ """Return the object path."""
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature="a{oa{sa{sv}}}")
+ def GetManagedObjects(self):
+ """Return a dictionary of all managed objects."""
+ resp = {}
+ for service in self.services:
+ resp[service.get_path()] = service.get_properties()
+ for ch in service.characteristics:
+ resp[ch.get_path()] = ch.get_properties()
+ return resp
+
+
+# =======================
+# Bluetooth Command Server
+# =======================
+class BluetoothCommandService:
+ """Bluetooth Command Service."""
+
+ def __init__(self, device_name="ReachyMini", pin_code="00000"):
+ """Initialize the Bluetooth Command Service."""
+ self.device_name = device_name
+ self.pin_code = pin_code
+ self.connected = False
+ self.bus = None
+ self.app = None
+ self.adv = None
+ self.mainloop = None
+
+ def _handle_command(self, value: bytes) -> str:
+ command_str = value.decode("utf-8").strip()
+ logger.info(f"Received command: {command_str}")
+ # Custom command handling
+ if command_str.upper() == "PING":
+ return "PONG"
+ elif command_str.upper() == "STATUS":
+ # exec a "sudo ls" command and print the result
+ try:
+ result = subprocess.run(["sudo", "ls"], capture_output=True, text=True)
+ logger.info(f"Command output: {result.stdout}")
+ except Exception as e:
+ logger.error(f"Error executing command: {e}")
+ return "OK: System running"
+ elif command_str.startswith("PIN_"):
+ pin = command_str[4:].strip()
+ if pin == self.pin_code:
+ self.connected = True
+ return "OK: Connected"
+ else:
+ return "ERROR: Incorrect PIN"
+
+ # else if command starts with "CMD_xxxxx" check if commands directory contains the said named script command xxxx.sh and run its, show output or/and send to read
+ elif command_str.startswith("CMD_"):
+ if not self.connected:
+ return "ERROR: Not connected. Please authenticate first."
+ try:
+ script_name = command_str[4:].strip() + ".sh"
+ script_path = os.path.join("commands", script_name)
+ if os.path.isfile(script_path):
+ try:
+ result = subprocess.run(
+ ["sudo", script_path], capture_output=True, text=True
+ )
+ logger.info(f"Command output: {result.stdout}")
+ except Exception as e:
+ logger.error(f"Error executing command: {e}")
+ else:
+ return f"ERROR: Command '{script_name}' not found"
+ except Exception as e:
+ logger.error(f"Error processing command: {e}")
+ return "ERROR: Command execution failed"
+ finally:
+ self.connected = False # reset connection after command
+ else:
+ return f"ECHO: {command_str}"
+
+ def start(self):
+ """Start the Bluetooth Command Service."""
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+
+ # BLE Agent registration
+ agent_manager = dbus.Interface(
+ self.bus.get_object("org.bluez", "/org/bluez"), "org.bluez.AgentManager1"
+ )
+ # agent = NoInputAgent(self.bus, AGENT_PATH)
+ agent_manager.RegisterAgent(AGENT_PATH, "NoInputNoOutput")
+ agent_manager.RequestDefaultAgent(AGENT_PATH)
+ logger.info("BLE Agent registered for Just Works pairing")
+
+ # Find adapter
+ adapter = self._find_adapter()
+ if not adapter:
+ raise Exception("Bluetooth adapter not found")
+
+ adapter_props = dbus.Interface(adapter, DBUS_PROP_IFACE)
+ adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(True))
+ adapter_props.Set("org.bluez.Adapter1", "Discoverable", dbus.Boolean(True))
+ adapter_props.Set("org.bluez.Adapter1", "DiscoverableTimeout", dbus.UInt32(0))
+ adapter_props.Set("org.bluez.Adapter1", "Pairable", dbus.Boolean(True))
+
+ # Register GATT application
+ service_manager = dbus.Interface(adapter, GATT_MANAGER_IFACE)
+ self.app = Application(self.bus, self._handle_command)
+ service_manager.RegisterApplication(
+ self.app.get_path(),
+ {},
+ reply_handler=lambda: logger.info("GATT app registered"),
+ error_handler=lambda e: logger.error(f"Failed to register GATT app: {e}"),
+ )
+
+ # Register advertisement
+ ad_manager = dbus.Interface(adapter, LE_ADVERTISING_MANAGER_IFACE)
+ self.adv = Advertisement(self.bus, 0, "peripheral", self.device_name)
+ self.adv.service_uuids = [SERVICE_UUID]
+ ad_manager.RegisterAdvertisement(
+ self.adv.get_path(),
+ {},
+ reply_handler=lambda: logger.info("Advertisement registered"),
+ error_handler=lambda e: logger.error(
+ f"Failed to register advertisement: {e}"
+ ),
+ )
+
+ logger.info(f"✓ Bluetooth service started as '{self.device_name}'")
+
+ def _find_adapter(self):
+ remote_om = dbus.Interface(
+ self.bus.get_object(BLUEZ_SERVICE_NAME, "/"), DBUS_OM_IFACE
+ )
+ objects = remote_om.GetManagedObjects()
+ for path, props in objects.items():
+ if GATT_MANAGER_IFACE in props and LE_ADVERTISING_MANAGER_IFACE in props:
+ return self.bus.get_object(BLUEZ_SERVICE_NAME, path)
+ return None
+
+ def run(self):
+ """Run the Bluetooth Command Service."""
+ self.start()
+ self.mainloop = GLib.MainLoop()
+ try:
+ logger.info("Running. Press Ctrl+C to exit...")
+ self.mainloop.run()
+ except KeyboardInterrupt:
+ logger.info("Shutting down...")
+ self.mainloop.quit()
+
+
+def get_pin() -> str:
+ """Extract the last 5 digits of the serial number from dfu-util -l output."""
+ default_pin = "46879"
+ try:
+ result = subprocess.run(["dfu-util", "-l"], capture_output=True, text=True)
+ lines = result.stdout.splitlines()
+ for line in lines:
+ if "serial=" in line:
+ # Extract serial number
+ serial_part = line.split("serial=")[-1].strip().strip('"')
+ if len(serial_part) >= 5:
+ return serial_part[-5:]
+ return default_pin # fallback if not found
+ except Exception as e:
+ logger.error(f"Error getting pin from serial: {e}")
+ return default_pin
+
+
+# =======================
+# Main
+# =======================
+def main():
+ """Run the Bluetooth Command Service."""
+ pin = get_pin()
+
+ bt_service = BluetoothCommandService(device_name="ReachyMini", pin_code=pin)
+ bt_service.run()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7d64481da27f96028ee3e2dbbeca1dfcd3b13cc0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+nmcli device disconnect wlan0
+sleep 5
+rfkill unblock wifi
+systemctl restart reachy-mini-daemon.service
+
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3cf28b38a4c0336e2a8825697277d331d6f86424
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+ systemctl restart reachy-mini-daemon.service
+
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d1ae109a34171fe99dc31cb57a668651f802b41d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+rm -rf /venvs/
+cp -r /restore/venvs/ /
+chown -R pollen:pollen /venvs
+systemctl restart reachy-mini-daemon.service
+
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fcd3ae24f495dab909787ebe6e7de9273b6e3034
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+SERVICE_NAME="reachy-mini-bluetooth"
+SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
+LAUNCHER_PATH="$SCRIPT_DIR/bluetooth_service.py"
+COMMANDS_DIR="$SCRIPT_DIR/commands"
+SERVICE_PATH=/bluetooth/bluetooth_service.py
+
+sudo cp "$LAUNCHER_PATH" "$SERVICE_PATH"
+sudo cp -r "$COMMANDS_DIR" /bluetooth/commands
+# Ensure Python script is executable
+sudo chmod +x "$SERVICE_PATH"
+
+# Create the systemd service file
+cat < /dev/null
+[Unit]
+Description=Reachy Mini Bluetooth GATT Service
+After=network.target bluetooth.target
+Requires=bluetooth.target
+
+[Service]
+Type=simple
+ExecStart=/bin/bash -c 'sudo /usr/sbin/rfkill unblock bluetooth && sleep 2 && /usr/bin/python3 /bluetooth/bluetooth_service.py'
+Restart=on-failure
+User=$(whoami)
+WorkingDirectory=$(dirname "$SERVICE_PATH")
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+# Reload systemd, enable and start the service
+sudo systemctl daemon-reload
+sudo systemctl enable --now "$SERVICE_NAME"
+
+echo "Service '$SERVICE_NAME' installed and started."
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/__init__.py b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b912651bde9762e02f4e3d332d9881f1fd9bd7b0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/__init__.py
@@ -0,0 +1 @@
+"""GPIO shutdown service package."""
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh
new file mode 100644
index 0000000000000000000000000000000000000000..4d8e10fa9b2fe7d13ac0bee48a3bca255e1c94f2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+SERVICE_NAME="gpio-shutdown-daemon"
+SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
+LAUNCHER_PATH="$SCRIPT_DIR/launcher.sh"
+
+# Create the service file
+cat < /dev/null
+[Unit]
+Description=Reachy Mini GPIO Shutdown Daemon
+After=multi-user.target
+
+[Service]
+Type=simple
+ExecStart=$LAUNCHER_PATH
+Restart=on-failure
+User=$(whoami)
+WorkingDirectory=$(dirname "$LAUNCHER_PATH")
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+chmod +x $LAUNCHER_PATH
+
+# Reload systemd, enable and start the service
+sudo systemctl daemon-reload
+sudo systemctl enable --now $SERVICE_NAME
+
+echo "Service '$SERVICE_NAME' installed and started."
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7d62c5124b6b470e5cf4a9c1bc992a78a65cb443
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+source /venvs/mini_daemon/bin/activate
+python -m reachy_mini.daemon.app.services.gpio_shutdown.shutdown_monitor
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c9788a4fb8266531e6128b382f311ba4d801356
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py
@@ -0,0 +1,20 @@
+"""Monitor GPIO24 for shutdown signal."""
+
+from signal import pause
+from subprocess import call
+
+from gpiozero import Button
+
+shutdown_button = Button(23, pull_up=False)
+
+
+def released() -> None:
+ """Handle shutdown button released."""
+ print("Shutdown button released, shutting down...")
+ call(["sudo", "shutdown", "-h", "now"])
+
+
+shutdown_button.when_released = released
+
+print("Monitoring GPIO23 for shutdown signal...")
+pause()
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/wireless/install_service.sh b/reachy_mini/src/reachy_mini/daemon/app/services/wireless/install_service.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9e9e9b1cebd043f3ac471fb0b6b2d4b96c748776
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/wireless/install_service.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+SERVICE_NAME="reachy-mini-daemon"
+SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
+LAUNCHER_PATH="$SCRIPT_DIR/launcher.sh"
+
+# Create the service file
+cat < /dev/null
+[Unit]
+Description=Reachy Mini AP Launcher Service
+After=network.target
+
+[Service]
+Type=simple
+ExecStart=$LAUNCHER_PATH
+Restart=on-failure
+User=$(whoami)
+WorkingDirectory=$(dirname "$LAUNCHER_PATH")
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+chmod +x $LAUNCHER_PATH
+
+# Reload systemd, enable and start the service
+sudo systemctl daemon-reload
+sudo systemctl enable --now $SERVICE_NAME
+
+echo "Service '$SERVICE_NAME' installed and started."
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/daemon/app/services/wireless/launcher.sh b/reachy_mini/src/reachy_mini/daemon/app/services/wireless/launcher.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bf971ba7291f76a4a3eb4ed46d8bf3fe869d1a82
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/app/services/wireless/launcher.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+source /venvs/mini_daemon/bin/activate
+export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:/opt/gst-plugins-rs/lib/aarch64-linux-gnu/
+python -m reachy_mini.daemon.app.main --wireless-version --no-autostart
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/__init__.py b/reachy_mini/src/reachy_mini/daemon/backend/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..38eef82b1e234c864824970b6f12ef30892496e9
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/__init__.py
@@ -0,0 +1 @@
+"""Backend module for Reachy Mini Daemon."""
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/abstract.py b/reachy_mini/src/reachy_mini/daemon/backend/abstract.py
new file mode 100644
index 0000000000000000000000000000000000000000..68eb3e16d0982a8e21805e2e5ec16f9468252876
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/abstract.py
@@ -0,0 +1,755 @@
+"""Base class for robot backends, simulated or real.
+
+This module defines the `Backend` class, which serves as a base for implementing
+different types of robot backends, whether they are simulated (like Mujoco) or real
+(connected via serial port). The class provides methods for managing joint positions,
+torque control, and other backend-specific functionalities.
+It is designed to be extended by subclasses that implement the specific behavior for
+each type of backend.
+"""
+
+import asyncio
+import json
+import logging
+import threading
+import time
+import typing
+from abc import abstractmethod
+from enum import Enum
+from pathlib import Path
+from typing import Annotated, Any, Dict, Optional
+
+import numpy as np
+import zenoh
+from numpy.typing import NDArray
+from scipy.spatial.transform import Rotation as R
+
+if typing.TYPE_CHECKING:
+ from reachy_mini.daemon.backend.mujoco.backend import MujocoBackendStatus
+ from reachy_mini.daemon.backend.robot.backend import RobotBackendStatus
+ from reachy_mini.kinematics import AnyKinematics
+from reachy_mini.media.audio_sounddevice import SoundDeviceAudio
+from reachy_mini.motion.goto import GotoMove
+from reachy_mini.motion.move import Move
+from reachy_mini.utils.constants import MODELS_ROOT_PATH, URDF_ROOT_PATH
+from reachy_mini.utils.interpolation import (
+ InterpolationTechnique,
+ distance_between_poses,
+ time_trajectory,
+)
+
+
+class MotorControlMode(str, Enum):
+ """Enum for motor control modes."""
+
+ Enabled = "enabled" # Torque ON and controlled in position
+ Disabled = "disabled" # Torque OFF
+ GravityCompensation = "gravity_compensation" # Torque ON and controlled in current to compensate for gravity
+
+
+class Backend:
+ """Base class for robot backends, simulated or real."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ check_collision: bool = False,
+ kinematics_engine: str = "AnalyticalKinematics",
+ ) -> None:
+ """Initialize the backend."""
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+
+ self.should_stop = threading.Event()
+ self.ready = threading.Event()
+
+ self.check_collision = (
+ check_collision # Flag to enable/disable collision checking
+ )
+ self.kinematics_engine = kinematics_engine
+
+ self.logger.info(f"Using {self.kinematics_engine} kinematics engine")
+
+ if self.check_collision:
+ assert self.kinematics_engine == "Placo", (
+ "Collision checking is only available with Placo Kinematics"
+ )
+
+ self.gravity_compensation_mode = False # Flag for gravity compensation mode
+
+ if self.gravity_compensation_mode:
+ assert self.kinematics_engine == "Placo", (
+ "Gravity compensation is only available with Placo kinematics"
+ )
+
+ if self.kinematics_engine == "Placo":
+ from reachy_mini.kinematics import PlacoKinematics
+
+ self.head_kinematics: AnyKinematics = PlacoKinematics(
+ URDF_ROOT_PATH, check_collision=self.check_collision
+ )
+ elif self.kinematics_engine == "NN":
+ from reachy_mini.kinematics import NNKinematics
+
+ self.head_kinematics = NNKinematics(MODELS_ROOT_PATH)
+ elif self.kinematics_engine == "AnalyticalKinematics":
+ from reachy_mini.kinematics import AnalyticalKinematics
+
+ self.head_kinematics = AnalyticalKinematics()
+ else:
+ raise ValueError(
+ f"Unknown kinematics engine: {self.kinematics_engine}. Use 'Placo', 'NN' or 'AnalyticalKinematics'."
+ )
+
+ self.current_head_pose: Annotated[NDArray[np.float64], (4, 4)] | None = (
+ None # 4x4 pose matrix
+ )
+ self.target_head_pose: Annotated[NDArray[np.float64], (4, 4)] | None = (
+ None # 4x4 pose matrix
+ )
+ self.target_body_yaw: float | None = (
+ None # Last body yaw used in IK computations
+ )
+
+ self.target_head_joint_positions: (
+ Annotated[NDArray[np.float64], (7,)] | None
+ ) = None # [yaw, 0, 1, 2, 3, 4, 5]
+ self.current_head_joint_positions: (
+ Annotated[NDArray[np.float64], (7,)] | None
+ ) = None # [yaw, 0, 1, 2, 3, 4, 5]
+ self.target_antenna_joint_positions: (
+ Annotated[NDArray[np.float64], (2,)] | None
+ ) = None # [0, 1]
+ self.current_antenna_joint_positions: (
+ Annotated[NDArray[np.float64], (2,)] | None
+ ) = None # [0, 1]
+
+ self.joint_positions_publisher: zenoh.Publisher | None = None
+ self.pose_publisher: zenoh.Publisher | None = None
+ self.recording_publisher: zenoh.Publisher | None = None
+ self.error: str | None = None # To store any error that occurs during execution
+ self.is_recording = False # Flag to indicate if recording is active
+ self.recorded_data: list[dict[str, Any]] = [] # List to store recorded data
+
+ # variables to store the last computed head joint positions and pose
+ self._last_target_body_yaw: float | None = (
+ None # Last body yaw used in IK computations
+ )
+ self._last_target_head_pose: Annotated[NDArray[np.float64], (4, 4)] | None = (
+ None # Last head pose used in IK computations
+ )
+ self.target_head_joint_current: Annotated[NDArray[np.float64], (7,)] | None = (
+ None # Placeholder for head joint torque
+ )
+ self.ik_required = False # Flag to indicate if IK computation is required
+
+ self.is_shutting_down = False
+
+ # Tolerance for kinematics computations
+ # For Forward kinematics (around 0.25deg)
+ # - FK is calculated at each timestep and is susceptible to noise
+ self._fk_kin_tolerance = 1e-3 # rads
+ # For Inverse kinematics (around 0.5mm and 0.1 degrees)
+ # - IK is calculated only when the head pose is set by the user
+ self._ik_kin_tolerance = {
+ "rad": 2e-3, # rads
+ "m": 0.5e-3, # m
+ }
+
+ # Recording lock to guard buffer swaps and appends
+ self._rec_lock = threading.Lock()
+
+ self.audio = SoundDeviceAudio(log_level=log_level)
+
+ # Life cycle methods
+ def wrapped_run(self) -> None:
+ """Run the backend in a try-except block to store errors."""
+ try:
+ self.run()
+ except Exception as e:
+ self.error = str(e)
+ self.close()
+ raise e
+
+ def run(self) -> None:
+ """Run the backend.
+
+ This method is a placeholder and should be overridden by subclasses.
+ """
+ raise NotImplementedError("The method run should be overridden by subclasses.")
+
+ def close(self) -> None:
+ """Close the backend.
+
+ This method is a placeholder and should be overridden by subclasses.
+ """
+ raise NotImplementedError(
+ "The method close should be overridden by subclasses."
+ )
+
+ def get_status(self) -> "RobotBackendStatus | MujocoBackendStatus":
+ """Return backend statistics.
+
+ This method is a placeholder and should be overridden by subclasses.
+ """
+ raise NotImplementedError(
+ "The method get_status should be overridden by subclasses."
+ )
+
+ # Present/Target joint positions
+ def set_joint_positions_publisher(self, publisher: zenoh.Publisher) -> None:
+ """Set the publisher for joint positions.
+
+ Args:
+ publisher: A publisher object that will be used to publish joint positions.
+
+ """
+ self.joint_positions_publisher = publisher
+
+ def set_pose_publisher(self, publisher: zenoh.Publisher) -> None:
+ """Set the publisher for head pose.
+
+ Args:
+ publisher: A publisher object that will be used to publish head pose.
+
+ """
+ self.pose_publisher = publisher
+
+ def update_target_head_joints_from_ik(
+ self,
+ pose: Annotated[NDArray[np.float64], (4, 4)] | None = None,
+ body_yaw: float | None = None,
+ ) -> None:
+ """Update the target head joint positions from inverse kinematics.
+
+ Args:
+ pose (np.ndarray): 4x4 pose matrix representing the head pose.
+ body_yaw (float): The yaw angle of the body, used to adjust the head pose.
+
+ """
+ if pose is None:
+ pose = (
+ self.target_head_pose
+ if self.target_head_pose is not None
+ else np.eye(4)
+ )
+
+ if body_yaw is None:
+ body_yaw = self.target_body_yaw if self.target_body_yaw is not None else 0.0
+
+ # Compute the inverse kinematics to get the head joint positions
+ joints = self.head_kinematics.ik(pose, body_yaw=body_yaw)
+ if joints is None or np.any(np.isnan(joints)):
+ raise ValueError("WARNING: Collision detected or head pose not achievable!")
+
+ # update the target head pose and body yaw
+ self._last_target_head_pose = pose
+ self._last_target_body_yaw = body_yaw
+
+ self.target_head_joint_positions = joints
+
+ def set_target_head_pose(
+ self,
+ pose: Annotated[NDArray[np.float64], (4, 4)],
+ ) -> None:
+ """Set the target head pose for the robot.
+
+ Args:
+ pose (np.ndarray): 4x4 pose matrix representing the head pose.
+
+ """
+ self.target_head_pose = pose
+ self.ik_required = True
+
+ def set_target_body_yaw(self, body_yaw: float) -> None:
+ """Set the target body yaw for the robot.
+
+ Only used when doing a set_target() with a standalone body_yaw (no head pose).
+
+ Args:
+ body_yaw (float): The yaw angle of the body
+
+ """
+ self.target_body_yaw = body_yaw
+ self.ik_required = True # Do we need that here?
+
+ def set_target_head_joint_positions(
+ self, positions: Annotated[NDArray[np.float64], (7,)] | None
+ ) -> None:
+ """Set the head joint positions.
+
+ Args:
+ positions (List[float]): A list of joint positions for the head.
+
+ """
+ self.target_head_joint_positions = positions
+ self.ik_required = False
+
+ def set_target(
+ self,
+ head: Annotated[NDArray[np.float64], (4, 4)] | None = None, # 4x4 pose matrix
+ antennas: Annotated[NDArray[np.float64], (2,)]
+ | None = None, # [right_angle, left_angle] (in rads)
+ body_yaw: float | None = None, # Body yaw angle in radians
+ ) -> None:
+ """Set the target head pose and/or antenna positions and/or body_yaw."""
+ if head is not None:
+ self.set_target_head_pose(head)
+
+ if body_yaw is not None:
+ self.set_target_body_yaw(body_yaw)
+
+ if antennas is not None:
+ self.set_target_antenna_joint_positions(antennas)
+
+ def set_target_antenna_joint_positions(
+ self,
+ positions: Annotated[NDArray[np.float64], (2,)],
+ ) -> None:
+ """Set the antenna joint positions.
+
+ Args:
+ positions (List[float]): A list of joint positions for the antenna.
+
+ """
+ self.target_antenna_joint_positions = positions
+
+ def set_target_head_joint_current(
+ self,
+ current: Annotated[NDArray[np.float64], (7,)],
+ ) -> None:
+ """Set the head joint current.
+
+ Args:
+ current (Annotated[NDArray[np.float64], (7,)]): A list of current values for the head motors.
+
+ """
+ self.target_head_joint_current = current
+ self.ik_required = False
+
+ async def play_move(
+ self,
+ move: Move,
+ play_frequency: float = 100.0,
+ initial_goto_duration: float = 0.0,
+ ) -> None:
+ """Asynchronously play a Move.
+
+ Args:
+ move (Move): The Move object to be played.
+ play_frequency (float): The frequency at which to evaluate the move (in Hz).
+ initial_goto_duration (float): Duration for an initial goto to the move's starting position. If 0.0, no initial goto is performed.
+
+ """
+ if initial_goto_duration > 0.0:
+ start_head_pose, start_antennas_positions, start_body_yaw = move.evaluate(
+ 0.0
+ )
+ await self.goto_target(
+ head=start_head_pose,
+ antennas=start_antennas_positions,
+ duration=initial_goto_duration,
+ body_yaw=start_body_yaw,
+ )
+ sleep_period = 1.0 / play_frequency
+
+ t0 = time.time()
+ while time.time() - t0 < move.duration:
+ t = time.time() - t0
+
+ head, antennas, body_yaw = move.evaluate(t)
+ if head is not None:
+ self.set_target_head_pose(head)
+ if body_yaw is not None:
+ self.set_target_body_yaw(body_yaw)
+ if antennas is not None:
+ self.set_target_antenna_joint_positions(antennas)
+
+ elapsed = time.time() - t0 - t
+ if elapsed < sleep_period:
+ await asyncio.sleep(sleep_period - elapsed)
+ else:
+ await asyncio.sleep(0.001)
+
+ async def goto_target(
+ self,
+ head: Annotated[NDArray[np.float64], (4, 4)] | None = None, # 4x4 pose matrix
+ antennas: Annotated[NDArray[np.float64], (2,)]
+ | None = None, # [right_angle, left_angle] (in rads)
+ duration: float = 0.5, # Duration in seconds for the movement, default is 0.5 seconds.
+ method: InterpolationTechnique = InterpolationTechnique.MIN_JERK, # can be "linear", "minjerk", "ease" or "cartoon", default is "minjerk"
+ body_yaw: float | None = 0.0, # Body yaw angle in radians
+ ) -> None:
+ """Asynchronously go to a target head pose and/or antennas position using task space interpolation, in "duration" seconds.
+
+ Args:
+ head (np.ndarray | None): 4x4 pose matrix representing the target head pose.
+ antennas (np.ndarray | list[float] | None): 1D array with two elements representing the angles of the antennas in radians.
+ duration (float): Duration of the movement in seconds.
+ method (str): Interpolation method to use ("linear", "minjerk", "ease", "cartoon"). Default is "minjerk".
+ body_yaw (float | None): Body yaw angle in radians.
+
+ Raises:
+ ValueError: If neither head nor antennas are provided, or if duration is not positive.
+
+ """
+ return await self.play_move(
+ move=GotoMove(
+ start_head_pose=self.get_present_head_pose(),
+ target_head_pose=head,
+ start_body_yaw=self.get_present_body_yaw(),
+ target_body_yaw=body_yaw,
+ start_antennas=np.array(self.get_present_antenna_joint_positions()),
+ target_antennas=np.array(antennas) if antennas is not None else None,
+ duration=duration,
+ method=method,
+ )
+ )
+
+ async def goto_joint_positions(
+ self,
+ head_joint_positions: list[float]
+ | None = None, # [yaw, stewart_platform x 6] length 7
+ antennas_joint_positions: list[float]
+ | None = None, # [right_angle, left_angle] length 2
+ duration: float = 0.5, # Duration in seconds for the movement
+ method: InterpolationTechnique = InterpolationTechnique.MIN_JERK, # can be "linear", "minjerk", "ease" or "cartoon", default is "minjerk"
+ ) -> None:
+ """Asynchronously go to a target head joint positions and/or antennas joint positions using joint space interpolation, in "duration" seconds.
+
+ Go to a target head joint positions and/or antennas joint positions using joint space interpolation, in "duration" seconds.
+
+ Args:
+ head_joint_positions (Optional[List[float]]): List of head joint positions in radians (length 7).
+ antennas_joint_positions (Optional[List[float]]): List of antennas joint positions in radians (length 2).
+ duration (float): Duration of the movement in seconds. Default is 0.5 seconds.
+ method (str): Interpolation method to use ("linear", "minjerk", "ease", "cartoon"). Default is "minjerk".
+
+ Raises:
+ ValueError: If neither head_joint_positions nor antennas_joint_positions are provided, or if duration is not positive.
+
+ """
+ if duration <= 0.0:
+ raise ValueError(
+ "Duration must be positive and non-zero. Use set_target() for immediate position setting."
+ )
+
+ start_head = np.array(self.get_present_head_joint_positions())
+ start_antennas = np.array(self.get_present_antenna_joint_positions())
+
+ target_head = (
+ np.array(head_joint_positions)
+ if head_joint_positions is not None
+ else start_head
+ )
+ target_antennas = (
+ np.array(antennas_joint_positions)
+ if antennas_joint_positions is not None
+ else start_antennas
+ )
+
+ t0 = time.time()
+ while time.time() - t0 < duration:
+ t = time.time() - t0
+
+ interp_time = time_trajectory(t / duration, method=method)
+
+ head_joint = start_head + (target_head - start_head) * interp_time
+ antennas_joint = (
+ start_antennas + (target_antennas - start_antennas) * interp_time
+ )
+
+ self.set_target_head_joint_positions(head_joint)
+ self.set_target_antenna_joint_positions(antennas_joint)
+ await asyncio.sleep(0.01)
+
+ def set_recording_publisher(self, publisher: zenoh.Publisher) -> None:
+ """Set the publisher for recording data.
+
+ Args:
+ publisher: A publisher object that will be used to publish recorded data.
+
+ """
+ self.recording_publisher = publisher
+
+ def append_record(self, record: dict[str, Any]) -> None:
+ """Append a record to the recorded data.
+
+ Args:
+ record (dict): A dictionary containing the record data to be appended.
+
+ """
+ if not self.is_recording:
+ return
+ # Double-check under lock to avoid race with stop_recording
+ with self._rec_lock:
+ if self.is_recording:
+ self.recorded_data.append(record)
+
+ def start_recording(self) -> None:
+ """Start recording data."""
+ with self._rec_lock:
+ self.recorded_data = []
+ self.is_recording = True
+
+ def stop_recording(self) -> None:
+ """Stop recording data and publish the recorded data."""
+ # Swap buffer under lock so writers cannot touch the published list
+ with self._rec_lock:
+ self.is_recording = False
+ recorded_data, self.recorded_data = self.recorded_data, []
+ # Publish outside the lock
+ if self.recording_publisher is not None:
+ self.recording_publisher.put(json.dumps(recorded_data))
+ else:
+ self.logger.warning(
+ "stop_recording called but recording_publisher is not set; dropping data."
+ )
+
+ def get_present_head_joint_positions(self) -> Annotated[NDArray[np.float64], (7,)]:
+ """Return the present head joint positions.
+
+ This method is a placeholder and should be overridden by subclasses.
+ """
+ raise NotImplementedError(
+ "The method get_present_head_joint_positions should be overridden by subclasses."
+ )
+
+ def get_present_body_yaw(self) -> float:
+ """Return the present body yaw."""
+ yaw: float = self.get_present_head_joint_positions()[0]
+ return yaw
+
+ def get_present_head_pose(self) -> Annotated[NDArray[np.float64], (4, 4)]:
+ """Return the present head pose as a 4x4 matrix."""
+ assert self.current_head_pose is not None, (
+ "The current head pose is not set. Please call the update_head_kinematics_model method first."
+ )
+ return self.current_head_pose
+
+ def get_current_head_pose(self) -> Annotated[NDArray[np.float64], (4, 4)]:
+ """Return the present head pose as a 4x4 matrix."""
+ return self.get_present_head_pose()
+
+ def get_present_antenna_joint_positions(
+ self,
+ ) -> Annotated[NDArray[np.float64], (2,)]:
+ """Return the present antenna joint positions.
+
+ This method is a placeholder and should be overridden by subclasses.
+ """
+ raise NotImplementedError(
+ "The method get_present_antenna_joint_positions should be overridden by subclasses."
+ )
+
+ # Kinematics methods
+ def update_head_kinematics_model(
+ self,
+ head_joint_positions: Annotated[NDArray[np.float64], (7,)] | None = None,
+ antennas_joint_positions: Annotated[NDArray[np.float64], (2,)] | None = None,
+ ) -> None:
+ """Update the placo kinematics of the robot.
+
+ Args:
+ head_joint_positions (List[float] | None): The joint positions of the head.
+ antennas_joint_positions (List[float] | None): The joint positions of the antennas.
+
+ Returns:
+ None: This method does not return anything.
+
+ This method updates the head kinematics model with the given joint positions.
+ - If the joint positions are not provided, it will use the current joint positions.
+ - If the head joint positions have not changed, it will return without recomputing the forward kinematics.
+ - If the head joint positions have changed, it will compute the forward kinematics to get the current head pose.
+ - If the forward kinematics fails, it will raise an assertion error.
+ - If the antennas joint positions are provided, it will update the current antenna joint positions.
+
+ Note:
+ This method will update the `current_head_pose` and `current_head_joint_positions`
+ attributes of the backend instance with the computed values. And the `current_antenna_joint_positions` if provided.
+
+ """
+ if head_joint_positions is None:
+ head_joint_positions = self.get_present_head_joint_positions()
+
+ # Compute the forward kinematics to get the current head pose
+ self.current_head_pose = self.head_kinematics.fk(head_joint_positions)
+
+ # Check if the FK was successful
+ assert self.current_head_pose is not None, (
+ "FK failed to compute the current head pose."
+ )
+
+ # Store the last head joint positions
+ self.current_head_joint_positions = head_joint_positions
+
+ if antennas_joint_positions is not None:
+ self.current_antenna_joint_positions = antennas_joint_positions
+
+ def set_automatic_body_yaw(self, body_yaw: bool) -> None:
+ """Set the automatic body yaw.
+
+ Args:
+ body_yaw (bool): The yaw angle of the body.
+
+ """
+ self.head_kinematics.set_automatic_body_yaw(automatic_body_yaw=body_yaw)
+
+ def get_urdf(self) -> str:
+ """Get the URDF representation of the robot."""
+ urdf_path = Path(URDF_ROOT_PATH) / "robot.urdf"
+
+ with open(urdf_path, "r") as f:
+ return f.read()
+
+ # Multimedia methods
+ def play_sound(self, sound_file: str) -> None:
+ """Play a sound file from the assets directory.
+
+ If the file is not found in the assets directory, try to load the path itself.
+
+ Args:
+ sound_file (str): The name of the sound file to play (e.g., "wake_up.wav").
+
+ """
+ self.audio.play_sound(sound_file, autoclean=True)
+
+ # Basic move definitions
+ INIT_HEAD_POSE = np.eye(4)
+
+ SLEEP_HEAD_JOINT_POSITIONS = [
+ 0,
+ -0.9848156658225817,
+ 1.2624661884298831,
+ -0.24390294527381684,
+ 0.20555342557667577,
+ -1.2363885150358267,
+ 1.0032234352772091,
+ ]
+
+ SLEEP_ANTENNAS_JOINT_POSITIONS = np.array((-3.05, 3.05))
+ SLEEP_HEAD_POSE = np.array(
+ [
+ [0.911, 0.004, 0.413, -0.021],
+ [-0.004, 1.0, -0.001, 0.001],
+ [-0.413, -0.001, 0.911, -0.044],
+ [0.0, 0.0, 0.0, 1.0],
+ ]
+ )
+
+ async def wake_up(self) -> None:
+ """Wake up the robot - go to the initial head position and play the wake up emote and sound."""
+ await asyncio.sleep(0.1)
+
+ _, _, magic_distance = distance_between_poses(
+ self.get_current_head_pose(), self.INIT_HEAD_POSE
+ )
+
+ await self.goto_target(
+ self.INIT_HEAD_POSE,
+ antennas=np.array((0.0, 0.0)),
+ duration=magic_distance * 20 / 1000, # ms_per_magic_mm = 10
+ )
+ await asyncio.sleep(0.1)
+
+ # Toudoum
+ self.play_sound("wake_up.wav")
+
+ # Roll 20° to the left
+ pose = self.INIT_HEAD_POSE.copy()
+ pose[:3, :3] = R.from_euler("xyz", [20, 0, 0], degrees=True).as_matrix()
+ await self.goto_target(pose, duration=0.2)
+
+ # Go back to the initial position
+ await self.goto_target(self.INIT_HEAD_POSE, duration=0.2)
+
+ async def goto_sleep(self) -> None:
+ """Put the robot to sleep by moving the head and antennas to a predefined sleep position.
+
+ - If we are already very close to the sleep position, we do nothing.
+ - If we are far from the sleep position:
+ - If we are far from the initial position, we move there first.
+ - If we are close to the initial position, we move directly to the sleep position.
+ """
+ # Magic units
+ _, _, dist_to_sleep_pose = distance_between_poses(
+ self.get_current_head_pose(), self.SLEEP_HEAD_POSE
+ )
+ _, _, dist_to_init_pose = distance_between_poses(
+ self.get_current_head_pose(), self.INIT_HEAD_POSE
+ )
+ sleep_time = 2.0
+
+ # Thresholds found empirically.
+ if dist_to_sleep_pose > 10:
+ if dist_to_init_pose > 30:
+ # Move to the initial position
+ await self.goto_target(
+ self.INIT_HEAD_POSE, antennas=np.array((0.0, 0.0)), duration=1
+ )
+ await asyncio.sleep(0.2)
+
+ self.play_sound("go_sleep.wav")
+
+ # Move to the sleep position
+ await self.goto_target(
+ self.SLEEP_HEAD_POSE,
+ antennas=self.SLEEP_ANTENNAS_JOINT_POSITIONS,
+ duration=2,
+ )
+ else:
+ # The sound doesn't play fully if we don't wait enough
+ self.play_sound("go_sleep.wav")
+ sleep_time += 3
+
+ self._last_head_pose = self.SLEEP_HEAD_POSE
+ await asyncio.sleep(sleep_time)
+
+ # Motor control modes
+ @abstractmethod
+ def get_motor_control_mode(self) -> MotorControlMode:
+ """Get the motor control mode."""
+ pass
+
+ @abstractmethod
+ def set_motor_control_mode(self, mode: MotorControlMode) -> None:
+ """Set the motor control mode."""
+ pass
+
+ @abstractmethod
+ def set_motor_torque_ids(self, ids: list[str], on: bool) -> None:
+ """Set the motor torque for specific motor names."""
+ pass
+
+ def get_present_passive_joint_positions(self) -> Optional[Dict[str, float]]:
+ """Get the present passive joint positions.
+
+ Requires the Placo kinematics engine.
+ """
+ # This is would be better, and fix mypy issues, but Placo is dynamically imported
+ # if not isinstance(self.head_kinematics, PlacoKinematics):
+ if self.kinematics_engine != "Placo":
+ return None
+ return {
+ "passive_1_x": self.head_kinematics.get_joint("passive_1_x"), # type: ignore [union-attr]
+ "passive_1_y": self.head_kinematics.get_joint("passive_1_y"), # type: ignore [union-attr]
+ "passive_1_z": self.head_kinematics.get_joint("passive_1_z"), # type: ignore [union-attr]
+ "passive_2_x": self.head_kinematics.get_joint("passive_2_x"), # type: ignore [union-attr]
+ "passive_2_y": self.head_kinematics.get_joint("passive_2_y"), # type: ignore [union-attr]
+ "passive_2_z": self.head_kinematics.get_joint("passive_2_z"), # type: ignore [union-attr]
+ "passive_3_x": self.head_kinematics.get_joint("passive_3_x"), # type: ignore [union-attr]
+ "passive_3_y": self.head_kinematics.get_joint("passive_3_y"), # type: ignore [union-attr]
+ "passive_3_z": self.head_kinematics.get_joint("passive_3_z"), # type: ignore [union-attr]
+ "passive_4_x": self.head_kinematics.get_joint("passive_4_x"), # type: ignore [union-attr]
+ "passive_4_y": self.head_kinematics.get_joint("passive_4_y"), # type: ignore [union-attr]
+ "passive_4_z": self.head_kinematics.get_joint("passive_4_z"), # type: ignore [union-attr]
+ "passive_5_x": self.head_kinematics.get_joint("passive_5_x"), # type: ignore [union-attr]
+ "passive_5_y": self.head_kinematics.get_joint("passive_5_y"), # type: ignore [union-attr]
+ "passive_5_z": self.head_kinematics.get_joint("passive_5_z"), # type: ignore [union-attr]
+ "passive_6_x": self.head_kinematics.get_joint("passive_6_x"), # type: ignore [union-attr]
+ "passive_6_y": self.head_kinematics.get_joint("passive_6_y"), # type: ignore [union-attr]
+ "passive_6_z": self.head_kinematics.get_joint("passive_6_z"), # type: ignore [union-attr]
+ "passive_7_x": self.head_kinematics.get_joint("passive_7_x"), # type: ignore [union-attr]
+ "passive_7_y": self.head_kinematics.get_joint("passive_7_y"), # type: ignore [union-attr]
+ "passive_7_z": self.head_kinematics.get_joint("passive_7_z"), # type: ignore [union-attr]
+ }
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/mujoco/__init__.py b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..78ed8a96513559c50a3be08c238f0eeb401ad9cc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/__init__.py
@@ -0,0 +1,36 @@
+"""MuJoCo Backend for Reachy Mini Daemon."""
+
+from dataclasses import dataclass
+
+try:
+ import mujoco # noqa: F401
+
+ from reachy_mini.daemon.backend.mujoco.backend import (
+ MujocoBackend,
+ MujocoBackendStatus,
+ )
+
+except ImportError:
+
+ class MujocoMockupBackend:
+ """Mockup class to avoid import errors when MuJoCo is not installed."""
+
+ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
+ """Raise ImportError when trying to instantiate the class."""
+ raise ImportError(
+ "MuJoCo is not installed. MuJoCo backend is not available."
+ " To use MuJoCo backend, please install the 'mujoco' extra dependencies"
+ " with 'pip install reachy_mini[mujoco]'."
+ )
+
+ MujocoBackend = MujocoMockupBackend # type: ignore[assignment, misc]
+
+ @dataclass
+ class MujocoMockupBackendStatus:
+ """Mockup class to avoid import errors when MuJoCo is not installed."""
+
+ pass
+
+ MujocoBackendStatus = MujocoMockupBackendStatus # type: ignore[assignment, misc]
+
+__all__ = ["MujocoBackend", "MujocoBackendStatus"]
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/mujoco/backend.py b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/backend.py
new file mode 100644
index 0000000000000000000000000000000000000000..5df66a6be070da4f04e8b1db2a32c8ba2bf2b23b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/backend.py
@@ -0,0 +1,333 @@
+"""Mujoco Backend for Reachy Mini.
+
+This module provides the MujocoBackend class for simulating the Reachy Mini robot using the MuJoCo physics engine.
+
+It includes methods for running the simulation, getting joint positions, and controlling the robot's joints.
+
+"""
+
+import json
+import time
+from dataclasses import dataclass
+from importlib.resources import files
+from threading import Thread
+from typing import Annotated
+
+import log_throttling
+import mujoco
+import mujoco.viewer
+import numpy as np
+import numpy.typing as npt
+
+import reachy_mini
+
+from ..abstract import Backend, MotorControlMode
+from .utils import (
+ get_actuator_names,
+ get_joint_addr_from_name,
+ get_joint_id_from_name,
+)
+from .video_udp import UDPJPEGFrameSender
+
+
+class MujocoBackend(Backend):
+ """Simulated Reachy Mini using MuJoCo."""
+
+ def __init__(
+ self,
+ scene: str = "empty",
+ check_collision: bool = False,
+ kinematics_engine: str = "AnalyticalKinematics",
+ headless: bool = False,
+ ) -> None:
+ """Initialize the MujocoBackend with a specified scene.
+
+ Args:
+ scene (str): The name of the scene to load. Default is "empty".
+ check_collision (bool): If True, enable collision checking. Default is False.
+ kinematics_engine (str): Kinematics engine to use. Defaults to "AnalyticalKinematics".
+ headless (bool): If True, run Mujoco in headless mode (no GUI). Default is False.
+
+ """
+ super().__init__(
+ check_collision=check_collision, kinematics_engine=kinematics_engine
+ )
+
+ self.headless = headless
+
+ from reachy_mini.reachy_mini import (
+ SLEEP_ANTENNAS_JOINT_POSITIONS,
+ SLEEP_HEAD_JOINT_POSITIONS,
+ )
+
+ # Real robot convention for the order of the antennas joints is [right, left], but in mujoco it's [left, right]
+ self._SLEEP_ANTENNAS_JOINT_POSITIONS = [
+ SLEEP_ANTENNAS_JOINT_POSITIONS[1],
+ SLEEP_ANTENNAS_JOINT_POSITIONS[0],
+ ]
+ self._SLEEP_HEAD_JOINT_POSITIONS = SLEEP_HEAD_JOINT_POSITIONS
+
+ mjcf_root_path = str(
+ files(reachy_mini).joinpath("descriptions/reachy_mini/mjcf/")
+ )
+ self.model = mujoco.MjModel.from_xml_path(
+ f"{mjcf_root_path}/scenes/{scene}.xml"
+ )
+ self.data = mujoco.MjData(self.model)
+ self.model.opt.timestep = 0.002 # s, simulation timestep, 500hz
+ self.decimation = 10 # -> 50hz control loop
+ self.rendering_timestep = 0.04 # s, rendering loop # 25Hz
+
+ self.camera_id = mujoco.mj_name2id(
+ self.model,
+ mujoco.mjtObj.mjOBJ_CAMERA,
+ "eye_camera",
+ )
+
+ self.head_site_id = mujoco.mj_name2id(
+ self.model,
+ mujoco.mjtObj.mjOBJ_SITE,
+ "head",
+ )
+
+ self.current_head_pose = np.eye(4)
+
+ self.joint_names = get_actuator_names(self.model)
+
+ self.joint_ids = [
+ get_joint_id_from_name(self.model, n) for n in self.joint_names
+ ]
+ self.joint_qpos_addr = [
+ get_joint_addr_from_name(self.model, n) for n in self.joint_names
+ ]
+
+ self.col_inds = []
+ for i, type in enumerate(self.model.geom_contype):
+ if type != 0:
+ self.col_inds.append(i)
+ self.model.geom_contype[i] = 0
+ self.model.geom_conaffinity[i] = 0
+
+ def rendering_loop(self) -> None:
+ """Offline Rendering loop for the Mujoco simulation.
+
+ Capture the image from the virtual Reachy's camera and send it over UDP.
+ """
+ streamer_udp = UDPJPEGFrameSender()
+ camera_size = (1280, 720)
+ offscreen_renderer = mujoco.Renderer(
+ self.model, height=camera_size[1], width=camera_size[0]
+ )
+ while not self.should_stop.is_set():
+ start_t = time.time()
+ offscreen_renderer.update_scene(self.data, self.camera_id)
+ im = offscreen_renderer.render()
+ streamer_udp.send_frame(im)
+
+ took = time.time() - start_t
+ time.sleep(max(0, self.rendering_timestep - took))
+
+ def run(self) -> None:
+ """Run the Mujoco simulation with a viewer.
+
+ This method initializes the viewer and enters the main simulation loop.
+ It updates the joint positions at a rate and publishes the joint positions.
+ """
+ step = 1
+ if not self.headless:
+ viewer = mujoco.viewer.launch_passive(
+ self.model, self.data, show_left_ui=False, show_right_ui=False
+ )
+ with viewer.lock():
+ viewer.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
+ viewer.cam.distance = 0.8 # ≃ ||pos - lookat||
+ viewer.cam.azimuth = 160 # degrees
+ viewer.cam.elevation = -20 # degrees
+ viewer.cam.lookat[:] = [0, 0, 0.15]
+
+ # force one render with your new camera
+ mujoco.mj_step(self.model, self.data)
+ viewer.sync()
+
+ # im = self.get_camera()
+ # self.streamer_udp.send_frame(im)
+
+ self.data.qpos[self.joint_qpos_addr] = np.array(
+ self._SLEEP_HEAD_JOINT_POSITIONS
+ + self._SLEEP_ANTENNAS_JOINT_POSITIONS
+ ).reshape(-1, 1)
+ self.data.ctrl[:] = np.array(
+ self._SLEEP_HEAD_JOINT_POSITIONS
+ + self._SLEEP_ANTENNAS_JOINT_POSITIONS
+ )
+
+ # recompute all kinematics, collisions, etc.
+ mujoco.mj_forward(self.model, self.data)
+
+ for i in range(100):
+ mujoco.mj_step(self.model, self.data)
+
+ # enable collisions
+ for i in self.col_inds:
+ self.model.geom_contype[i] = 1
+ self.model.geom_conaffinity[i] = 1
+
+ for i in range(100):
+ mujoco.mj_step(self.model, self.data)
+
+ # one more frame so the viewer shows your startup pose
+ mujoco.mj_step(self.model, self.data)
+ if not self.headless:
+ viewer.sync()
+
+ rendering_thread = Thread(target=self.rendering_loop, daemon=True)
+ rendering_thread.start()
+
+ # Update the internal states of the IK and FK to the current configuration
+ # This is important to avoid jumps when starting the robot (beore wake-up)
+ self.head_kinematics.ik(self.get_mj_present_head_pose(), no_iterations=20)
+ self.head_kinematics.fk(
+ self.get_present_head_joint_positions(), no_iterations=20
+ )
+
+ # 3) now enter your normal loop
+ while not self.should_stop.is_set():
+ start_t = time.time()
+
+ if step % self.decimation == 0:
+ # update the current states
+ self.current_head_joint_positions = (
+ self.get_present_head_joint_positions()
+ )
+ self.current_antenna_joint_positions = (
+ self.get_present_antenna_joint_positions()
+ )
+ # Update the Placo kinematics model to recompute passive joints
+ self.update_head_kinematics_model(
+ self.current_head_joint_positions,
+ self.current_antenna_joint_positions,
+ )
+ self.current_head_pose = self.get_mj_present_head_pose()
+
+ # Update the target head joint positions from IK if necessary
+ # - does nothing if the targets did not change
+ if self.ik_required:
+ try:
+ self.update_target_head_joints_from_ik(
+ self.target_head_pose, self.target_body_yaw
+ )
+ except ValueError as e:
+ log_throttling.by_time(self.logger, interval=0.5).warning(
+ f"IK error: {e}"
+ )
+
+ if self.target_head_joint_positions is not None:
+ self.data.ctrl[:7] = self.target_head_joint_positions
+ if self.target_antenna_joint_positions is not None:
+ self.data.ctrl[-2:] = -self.target_antenna_joint_positions
+
+ if (
+ self.joint_positions_publisher is not None
+ and self.pose_publisher is not None
+ ):
+ if not self.is_shutting_down:
+ self.joint_positions_publisher.put(
+ json.dumps(
+ {
+ "head_joint_positions": self.current_head_joint_positions.tolist(),
+ "antennas_joint_positions": self.current_antenna_joint_positions.tolist(),
+ }
+ ).encode("utf-8")
+ )
+ self.pose_publisher.put(
+ json.dumps(
+ {
+ "head_pose": self.get_present_head_pose().tolist(),
+ }
+ ).encode("utf-8")
+ )
+ self.ready.set()
+
+ if not self.headless:
+ viewer.sync()
+
+ mujoco.mj_step(self.model, self.data)
+
+ took = time.time() - start_t
+ time.sleep(max(0, self.model.opt.timestep - took))
+ # print(f"Step {step}: took {took*1e6:.1f}us")
+ step += 1
+
+ if not self.headless:
+ viewer.close()
+
+ def get_mj_present_head_pose(self) -> Annotated[npt.NDArray[np.float64], (4, 4)]:
+ """Get the current head pose from the Mujoco simulation.
+
+ Returns:
+ np.ndarray: The current head pose as a 4x4 transformation matrix.
+
+ """
+ mj_current_head_pose = np.eye(4)
+
+ mj_current_head_pose[:3, :3] = self.data.site_xmat[self.head_site_id].reshape(
+ 3, 3
+ )
+ mj_current_head_pose[:3, 3] = self.data.site_xpos[self.head_site_id]
+ mj_current_head_pose[2, 3] -= 0.177
+ return mj_current_head_pose
+
+ def close(self) -> None:
+ """Close the Mujoco backend."""
+ # TODO Do something in mujoco here ?
+ pass
+
+ def get_status(self) -> "MujocoBackendStatus":
+ """Get the status of the Mujoco backend.
+
+ Returns:
+ dict: An empty dictionary as the Mujoco backend does not have a specific status to report.
+
+ """
+ return MujocoBackendStatus(motor_control_mode=self.get_motor_control_mode())
+
+ def get_present_head_joint_positions(
+ self,
+ ) -> Annotated[npt.NDArray[np.float64], (7,)]:
+ """Get the current joint positions of the head."""
+ pos: npt.NDArray[np.float64] = self.data.qpos[
+ self.joint_qpos_addr[:7]
+ ].flatten()
+ return pos
+
+ def get_present_antenna_joint_positions(
+ self,
+ ) -> Annotated[npt.NDArray[np.float64], (2,)]:
+ """Get the current joint positions of the antennas."""
+ pos: npt.NDArray[np.float64] = self.data.qpos[
+ self.joint_qpos_addr[-2:]
+ ].flatten()
+ return -pos
+
+ def get_motor_control_mode(self) -> MotorControlMode:
+ """Get the motor control mode."""
+ return MotorControlMode.Enabled
+
+ def set_motor_control_mode(self, mode: MotorControlMode) -> None:
+ """Set the motor control mode."""
+ pass
+
+ def set_motor_torque_ids(self, ids: list[str], on: bool) -> None:
+ """Set the motor torque state for specific motor names."""
+ pass
+
+
+@dataclass
+class MujocoBackendStatus:
+ """Dataclass to represent the status of the Mujoco backend.
+
+ Empty for now, as the Mujoco backend does not have a specific status to report.
+ """
+
+ motor_control_mode: MotorControlMode
+ error: str | None = None
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/mujoco/utils.py b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..c85b764393a438ab4c7998446453e1609b6f56ce
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/utils.py
@@ -0,0 +1,59 @@
+"""Mujoco utilities for Reachy Mini.
+
+This module provides utility functions for working with MuJoCo models, including
+homogeneous transformation matrices, joint positions, and actuator names.
+"""
+
+from typing import Annotated
+
+import mujoco
+import numpy as np
+import numpy.typing as npt
+from mujoco._structs import MjData, MjModel
+from scipy.spatial.transform import Rotation as R
+
+
+def get_homogeneous_matrix_from_euler(
+ position: tuple[float, float, float] = (0, 0, 0), # (x, y, z) meters
+ euler_angles: tuple[float, float, float] = (0, 0, 0), # (roll, pitch, yaw)
+ degrees: bool = False,
+) -> Annotated[npt.NDArray[np.float64], (4, 4)]:
+ """Return a homogeneous transformation matrix from position and Euler angles."""
+ homogeneous_matrix = np.eye(4)
+ homogeneous_matrix[:3, :3] = R.from_euler(
+ "xyz", euler_angles, degrees=degrees
+ ).as_matrix()
+ homogeneous_matrix[:3, 3] = position
+ return homogeneous_matrix
+
+
+def get_joint_qpos(model: MjModel, data: MjData, joint_name: str) -> float:
+ """Return the qpos (rad) of a specified joint in the model."""
+ # Get the joint id
+ joint_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, joint_name)
+ if joint_id == -1:
+ raise ValueError(f"Joint '{joint_name}' not found.")
+
+ # Get the address of the joint's qpos in the qpos array
+ qpos_addr = model.jnt_qposadr[joint_id]
+
+ # Get the qpos value
+ qpos: float = data.qpos[qpos_addr]
+ return qpos
+
+
+def get_joint_id_from_name(model: MjModel, name: str) -> int:
+ """Return the id of a specified joint."""
+ return mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, name) # type: ignore
+
+
+def get_joint_addr_from_name(model: MjModel, name: str) -> int:
+ """Return the address of a specified joint."""
+ addr: int = model.joint(name).qposadr
+ return addr
+
+
+def get_actuator_names(model: MjModel) -> list[str]:
+ """Return the list of the actuators names from the MuJoCo model."""
+ actuator_names = [model.actuator(k).name for k in range(0, model.nu)]
+ return actuator_names
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/mujoco/video_udp.py b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/video_udp.py
new file mode 100644
index 0000000000000000000000000000000000000000..087ae9784de41b934f41b9ed2bbeadf391eacdd5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/mujoco/video_udp.py
@@ -0,0 +1,53 @@
+"""UDP JPEG Frame Sender.
+
+This module provides a class to send JPEG frames over UDP. It encodes the frames as JPEG images and splits them into chunks to fit within the maximum packet size for UDP transmission.
+"""
+
+import socket
+import struct
+
+import cv2
+import numpy as np
+import numpy.typing as npt
+
+
+class UDPJPEGFrameSender:
+ """A class to send JPEG frames over UDP."""
+
+ def __init__(
+ self,
+ dest_ip: str = "127.0.0.1",
+ dest_port: int = 5005,
+ max_packet_size: int = 1400,
+ ) -> None:
+ """Initialize the UDPJPEGFrameSender.
+
+ Args:
+ dest_ip (str): Destination IP address.
+ dest_port (int): Destination port number.
+ max_packet_size (int): Maximum size of each UDP packet.
+
+ """
+ self.addr = (dest_ip, dest_port)
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.max_packet_size = max_packet_size
+
+ def send_frame(self, frame: npt.NDArray[np.uint8]) -> None:
+ """Send a frame as a JPEG image over UDP.
+
+ Args:
+ frame (np.ndarray): The frame to be sent, in RGB format.
+
+ """
+ frame_cvt = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
+ ret, jpeg_bytes = cv2.imencode(
+ ".jpg", frame_cvt, [int(cv2.IMWRITE_JPEG_QUALITY), 80]
+ )
+ data = jpeg_bytes.tobytes()
+ total_size = len(data)
+ n_chunks = (total_size + self.max_packet_size - 1) // self.max_packet_size
+ self.sock.sendto(struct.pack("!II", n_chunks, total_size), self.addr)
+ for i in range(n_chunks):
+ start = i * self.max_packet_size
+ end = min(start + self.max_packet_size, total_size)
+ self.sock.sendto(data[start:end], self.addr)
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/robot/__init__.py b/reachy_mini/src/reachy_mini/daemon/backend/robot/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b6ffe66ceea9f718065a88b9836bcdc80c7f144
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/robot/__init__.py
@@ -0,0 +1,8 @@
+"""Real robot backend for Reachy Mini."""
+
+from reachy_mini.daemon.backend.robot.backend import (
+ RobotBackend,
+ RobotBackendStatus,
+)
+
+__all__ = ["RobotBackend", "RobotBackendStatus"]
diff --git a/reachy_mini/src/reachy_mini/daemon/backend/robot/backend.py b/reachy_mini/src/reachy_mini/daemon/backend/robot/backend.py
new file mode 100644
index 0000000000000000000000000000000000000000..03440f9746ef0d22cb454e8e9358aa627ac6071f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/backend/robot/backend.py
@@ -0,0 +1,590 @@
+"""Robot Backend for Reachy Mini.
+
+This module provides the `RobotBackend` class, which interfaces with the Reachy Mini motor controller to control the robot's movements and manage its status.
+It handles the control loop, joint positions, torque enabling/disabling, and provides a status report of the robot's backend.
+It uses the `ReachyMiniMotorController` to communicate with the robot's motors.
+"""
+
+import json
+import logging
+import struct
+import time
+from dataclasses import dataclass
+from datetime import timedelta
+from multiprocessing import Event # It seems to be more accurate than threading.Event
+from typing import Annotated, Any
+
+import log_throttling
+import numpy as np
+import numpy.typing as npt
+from reachy_mini_motor_controller import ReachyMiniPyControlLoop
+
+from reachy_mini.utils.hardware_config.parser import parse_yaml_config
+
+from ..abstract import Backend, MotorControlMode
+
+
+class RobotBackend(Backend):
+ """Real robot backend for Reachy Mini."""
+
+ def __init__(
+ self,
+ serialport: str,
+ log_level: str = "INFO",
+ check_collision: bool = False,
+ kinematics_engine: str = "AnalyticalKinematics",
+ hardware_error_check_frequency: float = 1.0,
+ hardware_config_filepath: str | None = None,
+ ):
+ """Initialize the RobotBackend.
+
+ Args:
+ serialport (str): The serial port to which the Reachy Mini is connected.
+ log_level (str): The logging level for the backend. Default is "INFO".
+ check_collision (bool): If True, enable collision checking. Default is False.
+ kinematics_engine (str): Kinematics engine to use. Defaults to "AnalyticalKinematics".
+ hardware_error_check_frequency (float): Frequency in seconds to check for hardware errors. Default is 1.0.
+ hardware_config_filepath (str | None): Path to the hardware configuration YAML file. Default is None.
+
+ Tries to connect to the Reachy Mini motor controller and initializes the control loop.
+
+ """
+ super().__init__(
+ check_collision=check_collision, kinematics_engine=kinematics_engine
+ )
+
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+
+ self.control_loop_frequency = 50.0 # Hz
+ self.c: ReachyMiniPyControlLoop | None = ReachyMiniPyControlLoop(
+ serialport,
+ read_position_loop_period=timedelta(
+ seconds=1.0 / self.control_loop_frequency
+ ),
+ allowed_retries=5,
+ stats_pub_period=None,
+ )
+
+ self.name2id = self.c.get_motor_name_id()
+ if hardware_config_filepath is not None:
+ config = parse_yaml_config(hardware_config_filepath)
+ for motor_name, motor_conf in config.motors.items():
+ if motor_conf.pid is not None:
+ motor_id = self.name2id[motor_name]
+ p, i, d = motor_conf.pid
+ self.logger.info(
+ f"Setting PID gains for motor '{motor_name}' (ID: {motor_id}): P={p}, I={i}, D={d}"
+ )
+ self.c.async_write_pid_gains(motor_id, p, i, d)
+
+ self.motor_control_mode = self._infer_control_mode()
+ self._torque_enabled = self.motor_control_mode != MotorControlMode.Disabled
+ self.logger.info(f"Motor control mode: {self.motor_control_mode}")
+ self.last_alive: float | None = None
+
+ self._status = RobotBackendStatus(
+ motor_control_mode=self.motor_control_mode,
+ ready=False,
+ last_alive=None,
+ control_loop_stats={},
+ )
+ self._stats_record_period = 1.0 # seconds
+ self._stats: dict[str, Any] = {
+ "timestamps": [],
+ "nb_error": 0,
+ "record_period": self._stats_record_period,
+ }
+
+ self._current_head_operation_mode = -1 # Default to torque control mode
+ self._current_antennas_operation_mode = -1 # Default to torque control mode
+ self.target_antenna_joint_current = None # Placeholder for antenna joint torque
+ self.target_head_joint_current = None # Placeholder for head joint torque
+
+ self.hardware_error_check_frequency = hardware_error_check_frequency # seconds
+
+ def run(self) -> None:
+ """Run the control loop for the robot backend.
+
+ This method continuously updates the motor controller at a specified frequency.
+ It reads the joint positions, updates the motor controller, and publishes the joint positions.
+ It also handles errors and retries if the motor controller is not responding.
+ """
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ period = 1.0 / self.control_loop_frequency # Control loop period in seconds
+
+ self.retries = 5
+ self.stats_record_t0 = time.time()
+
+ self.last_hardware_error_check_time = time.time()
+
+ next_call_event = Event()
+
+ # Compute the forward kinematics to get the initial head pose
+ # IMPORTANT for wake_up
+ head_positions, _ = self.get_all_joint_positions()
+ # make sure to converge fully (a lot of iterations)
+ self.current_head_pose = self.head_kinematics.fk(
+ np.array(head_positions),
+ no_iterations=20,
+ )
+ assert self.current_head_pose is not None
+
+ self.head_kinematics.ik(self.current_head_pose, no_iterations=20)
+
+ while not self.should_stop.is_set():
+ start_t = time.time()
+ self._stats["timestamps"].append(time.time())
+ self._update()
+ took = time.time() - start_t
+
+ sleep_time = period - took
+ if sleep_time < 0:
+ self.logger.debug(
+ f"Control loop took too long: {took * 1000:.3f} ms, expected {period * 1000:.3f} ms"
+ )
+ sleep_time = 0.001
+
+ next_call_event.clear()
+ next_call_event.wait(sleep_time)
+
+ def _update(self) -> None:
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ if self._torque_enabled:
+ if self._current_head_operation_mode != 0: # if position control mode
+ if self.target_head_joint_positions is not None:
+ self.c.set_stewart_platform_position(
+ self.target_head_joint_positions[1:].tolist()
+ )
+ self.c.set_body_rotation(self.target_head_joint_positions[0])
+ else: # it's in torque control mode
+ if self.gravity_compensation_mode:
+ # This function will set the head_joint_current
+ # to the current necessary to compensate for gravity
+ self.compensate_head_gravity()
+ if self.target_head_joint_current is not None:
+ self.c.set_stewart_platform_goal_current(
+ np.round(self.target_head_joint_current[1:], 0)
+ .astype(int)
+ .tolist()
+ )
+ # Body rotation torque control is not supported with feetech motors
+ # self.c.set_body_rotation_goal_current(int(self.target_head_joint_current[0]))
+
+ if self._current_antennas_operation_mode != 0: # if position control mode
+ if self.target_antenna_joint_positions is not None:
+ self.c.set_antennas_positions(
+ self.target_antenna_joint_positions.tolist()
+ )
+ # Antenna torque control is not supported with feetech motors
+ # else:
+ # if self.target_antenna_joint_current is not None:
+ # self.c.set_antennas_goal_current(
+ # np.round(self.target_antenna_joint_current, 0).astype(int).tolist()
+ # )
+
+ if (
+ self.joint_positions_publisher is not None
+ and self.pose_publisher is not None
+ ):
+ try:
+ head_positions, antenna_positions = self.get_all_joint_positions()
+
+ # Update the head kinematics model with the current head positions
+ self.update_head_kinematics_model(
+ np.array(head_positions),
+ np.array(antenna_positions),
+ )
+
+ # Update the target head joint positions from IK if necessary
+ # - does nothing if the targets did not change
+ if self.ik_required:
+ try:
+ self.update_target_head_joints_from_ik(
+ self.target_head_pose, self.target_body_yaw
+ )
+ except ValueError as e:
+ log_throttling.by_time(self.logger, interval=0.5).warning(
+ f"IK error: {e}"
+ )
+
+ if not self.is_shutting_down:
+ self.joint_positions_publisher.put(
+ json.dumps(
+ {
+ "head_joint_positions": head_positions,
+ "antennas_joint_positions": antenna_positions,
+ }
+ )
+ )
+ self.pose_publisher.put(
+ json.dumps(
+ {
+ "head_pose": self.get_present_head_pose().tolist(),
+ }
+ )
+ )
+
+ self.last_alive = time.time()
+
+ self.ready.set() # Mark the backend as ready
+ except RuntimeError as e:
+ self._stats["nb_error"] += 1
+
+ assert self.last_alive is not None
+
+ if self.last_alive + 1 < time.time():
+ self.error = (
+ "No response from the robot's motor for the last second."
+ )
+
+ self.logger.error(
+ "No response from the robot for the last second, stopping."
+ )
+ raise e
+
+ if time.time() - self.stats_record_t0 > self._stats_record_period:
+ dt = np.diff(self._stats["timestamps"])
+ if len(dt) > 1:
+ self._status.control_loop_stats["mean_control_loop_frequency"] = (
+ float(np.mean(1.0 / dt))
+ )
+ self._status.control_loop_stats["max_control_loop_interval"] = (
+ float(np.max(dt))
+ )
+ self._status.control_loop_stats["nb_error"] = self._stats[
+ "nb_error"
+ ]
+
+ self._stats["timestamps"].clear()
+ self._stats["nb_error"] = 0
+ self.stats_record_t0 = time.time()
+
+ if (
+ time.time() - self.last_hardware_error_check_time
+ > self.hardware_error_check_frequency
+ ):
+ hardware_errors = self.read_hardware_errors()
+ if hardware_errors:
+ for motor_name, errors in hardware_errors.items():
+ self.logger.error(
+ f"Motor '{motor_name}' hardware errors: {errors}"
+ )
+ self.last_hardware_error_check_time = time.time()
+
+ def close(self) -> None:
+ """Close the motor controller connection."""
+ if self.c is not None:
+ self.c.close()
+ self.c = None
+
+ def get_status(self) -> "RobotBackendStatus":
+ """Get the current status of the robot backend."""
+ self._status.error = self.error
+ self._status.motor_control_mode = self.motor_control_mode
+ return self._status
+
+ def enable_motors(self) -> None:
+ """Enable the motors by turning the torque on."""
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ self.c.enable_torque()
+ self._torque_enabled = True
+
+ def disable_motors(self) -> None:
+ """Disable the motors by turning the torque off."""
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ self.c.disable_torque()
+ self._torque_enabled = False
+
+ def set_head_operation_mode(self, mode: int) -> None:
+ """Change the operation mode of the head motors.
+
+ Args:
+ mode (int): The operation mode for the head motors.
+
+ The operation modes can be:
+ 0: torque control
+ 3: position control
+ 5: current-based position control.
+
+ Important:
+ This method does not work well with the current feetech motors (body rotation), as they do not support torque control.
+ So the method disables the antennas when in torque control mode.
+ The dynamixel motors used for the head do support torque control, so this method works as expected.
+
+ Args:
+ mode (int): The operation mode for the head motors.
+ This could be a specific mode like position control, velocity control, or torque control.
+
+ """
+ assert self.c is not None, "Motor controller not initialized or already closed."
+ assert mode in [0, 3, 5], (
+ "Invalid operation mode. Must be one of [0 (torque), 3 (position), 5 (current-limiting position)]."
+ f" Got {mode} instead"
+ )
+
+ # if motors are enabled, disable them before changing the mode
+ if self._torque_enabled:
+ self.c.enable_stewart_platform(False)
+ # set the new operation mode
+ self.c.set_stewart_platform_operating_mode(mode)
+
+ if mode != 0:
+ # if the mode is not torque control, we need to set the head joint positions
+ # to the current positions to avoid sudden movements
+ motor_pos = self.c.get_last_position()
+ self.target_head_joint_positions = np.array(
+ [motor_pos.body_yaw] + motor_pos.stewart
+ )
+
+ self.c.set_stewart_platform_position(
+ self.target_head_joint_positions[1:].tolist()
+ )
+ self.c.set_body_rotation(self.target_head_joint_positions[0])
+ self.c.enable_body_rotation(True)
+ self.c.set_body_rotation_operating_mode(0)
+ else:
+ self.c.enable_body_rotation(False)
+
+ if self._torque_enabled:
+ self.c.enable_stewart_platform(True)
+
+ self._current_head_operation_mode = mode
+
+ def set_antennas_operation_mode(self, mode: int) -> None:
+ """Change the operation mode of the antennas motors.
+
+ Args:
+ mode (int): The operation mode for the antennas motors (0: torque control, 3: position control, 5: current-based position control).
+
+ Important:
+ This method does not work well with the current feetech motors, as they do not support torque control.
+ So the method disables the antennas when in torque control mode.
+
+ Args:
+ mode (int): The operation mode for the antennas motors.
+ This could be a specific mode like position control, velocity control, or torque control.
+
+ """
+ assert self.c is not None, "Motor controller not initialized or already closed."
+ assert mode in [0, 3, 5], (
+ "Invalid operation mode. Must be one of [0 (torque), 3 (position), 5 (current-limiting position)]."
+ )
+
+ if self._current_antennas_operation_mode != mode:
+ if mode != 0:
+ # if the mode is not torque control, we need to set the head joint positions
+ # to the current positions to avoid sudden movements
+ self.target_antenna_joint_positions = np.array(
+ self.c.get_last_position().antennas
+ )
+ self.c.set_antennas_positions(
+ self.target_antenna_joint_positions.tolist()
+ )
+ self.c.enable_antennas(True)
+ else:
+ self.c.enable_antennas(False)
+
+ self._current_antennas_operation_mode = mode
+
+ def get_all_joint_positions(self) -> tuple[list[float], list[float]]:
+ """Get the current joint positions of the robot.
+
+ Returns:
+ tuple: A tuple containing two lists - the first list is for the head joint positions,
+ and the second list is for the antenna joint positions.
+
+ """
+ assert self.c is not None, "Motor controller not initialized or already closed."
+ positions = self.c.get_last_position()
+
+ yaw = positions.body_yaw
+ antennas = positions.antennas
+ dofs = positions.stewart
+
+ return [yaw] + list(dofs), list(antennas)
+
+ def get_present_head_joint_positions(
+ self,
+ ) -> Annotated[npt.NDArray[np.float64], (7,)]:
+ """Get the current joint positions of the head.
+
+ Returns:
+ list: A list of joint positions for the head, including the body rotation.
+
+ """
+ return np.array(self.get_all_joint_positions()[0])
+
+ def get_present_antenna_joint_positions(
+ self,
+ ) -> Annotated[npt.NDArray[np.float64], (2,)]:
+ """Get the current joint positions of the antennas.
+
+ Returns:
+ list: A list of joint positions for the antennas.
+
+ """
+ return np.array(self.get_all_joint_positions()[1])
+
+ def compensate_head_gravity(self) -> None:
+ """Calculate the currents necessary to compensate for gravity."""
+ assert self.kinematics_engine == "Placo", (
+ "Gravity compensation is only supported with the Placo kinematics engine."
+ )
+
+ # Even though in their docs dynamixes says that 1 count is 1 mA, in practice I've found it to be 3mA.
+ # I am not sure why this happens
+ # Another explanation is that our model is bad and the current is overestimated 3x (but I have not had these issues with other robots)
+ # So I am using a magic number to compensate for this.
+ # for currents under 30mA the constant is around 1
+ from_Nm_to_mA = 1.47 / 0.52 * 1000
+ # Conversion factor from Nm to mA for the Stewart platform motors
+ # The torque constant is not linear, so we need to use a correction factor
+ # This is a magic number that should be determined experimentally
+ # For currents under 30mA, the constant is around 4.0
+ # Then it drops to 1.0 for currents above 1.5A
+ correction_factor = 4.0
+ # Get the current head joint positions
+ head_joints = self.get_present_head_joint_positions()
+ gravity_torque = self.head_kinematics.compute_gravity_torque( # type: ignore [union-attr]
+ np.array(head_joints)
+ )
+ # Convert the torque from Nm to mA
+ current = gravity_torque * from_Nm_to_mA / correction_factor
+ # Set the head joint current
+ self.set_target_head_joint_current(current)
+
+ def get_motor_control_mode(self) -> MotorControlMode:
+ """Get the motor control mode."""
+ return self.motor_control_mode
+
+ def set_motor_control_mode(self, mode: MotorControlMode) -> None:
+ """Set the motor control mode."""
+ # Check if the mode is already set
+ if mode == self.motor_control_mode:
+ return
+
+ if mode == MotorControlMode.Enabled:
+ if self.motor_control_mode == MotorControlMode.GravityCompensation:
+ # First, make sure we switch to position control
+ self.disable_motors()
+ self.set_head_operation_mode(3)
+ self.set_antennas_operation_mode(3)
+
+ self.gravity_compensation_mode = False
+ self.enable_motors()
+
+ elif mode == MotorControlMode.Disabled:
+ self.gravity_compensation_mode = False
+ self.disable_motors()
+
+ elif mode == MotorControlMode.GravityCompensation:
+ if self.kinematics_engine != "Placo":
+ raise RuntimeError(
+ "Gravity compensation mode is only supported with the Placo kinematics engine."
+ )
+
+ self.disable_motors()
+ self.set_head_operation_mode(0)
+ self.set_antennas_operation_mode(0)
+ self.gravity_compensation_mode = True
+ self.enable_motors()
+
+ self.motor_control_mode = mode
+
+ def set_motor_torque_ids(self, ids: list[str], on: bool) -> None:
+ """Set the torque state for specific motor names.
+
+ Args:
+ ids (list[int]): List of motor IDs to set the torque state for.
+ on (bool): True to enable torque, False to disable.
+
+ """
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ assert ids is not None and len(ids) > 0, "IDs list cannot be empty or None."
+
+ ids_int = [self.name2id[name] for name in ids]
+
+ if on:
+ self.c.enable_torque_on_ids(ids_int)
+ else:
+ self.c.disable_torque_on_ids(ids_int)
+
+ def _infer_control_mode(self) -> MotorControlMode:
+ assert self.c is not None, "Motor controller not initialized or already closed."
+
+ torque = self.c.is_torque_enabled()
+
+ if not torque:
+ return MotorControlMode.Disabled
+
+ mode = self.c.get_stewart_platform_operating_mode()
+ if mode == 3:
+ return MotorControlMode.Enabled
+ elif mode == 1:
+ return MotorControlMode.GravityCompensation
+ else:
+ raise ValueError(f"Unknown motor control mode: {mode}")
+
+ def read_hardware_errors(self) -> dict[str, list[str]]:
+ """Read hardware errors from the motor controller."""
+ if self.c is None:
+ return {}
+
+ def decode_hardware_error_byte(err_byte: int) -> list[str]:
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#hardware-error-status
+ bits_to_error = {
+ 0: "Input Voltage Error",
+ 2: "Overheating Error",
+ 4: "Electrical Shock Error",
+ 5: "Overload Error",
+ }
+ err_bits = [i for i in range(8) if (err_byte & (1 << i)) != 0]
+ return [bits_to_error[b] for b in err_bits if b in bits_to_error]
+
+ def voltage_ok(
+ id: int,
+ allowed_max_voltage: float = 7.3,
+ ) -> bool:
+ assert self.c is not None, (
+ "Motor controller not initialized or already closed."
+ )
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#present-input-voltage
+ resp_bytes = self.c.async_read_raw_bytes(id, 144, 2)
+ resp = struct.unpack("h", bytes(resp_bytes))[0]
+ voltage: float = resp / 10.0 # in Volts
+
+ return voltage <= allowed_max_voltage
+
+ errors = {}
+ for name, id in self.c.get_motor_name_id().items():
+ # https://emanual.robotis.com/docs/en/dxl/x/xl330-m288/#hardware-error-status
+ err_byte = self.c.async_read_raw_bytes(id, 70, 1)
+ assert len(err_byte) == 1
+ err = decode_hardware_error_byte(err_byte[0])
+ if err:
+ if "Input Voltage Error" in err:
+ if voltage_ok(id):
+ err.remove("Input Voltage Error")
+
+ # To avoid logging empty errors like "Motor 1: []"
+ if len(err) > 0:
+ errors[name] = err
+
+ return errors
+
+
+@dataclass
+class RobotBackendStatus:
+ """Status of the Robot Backend."""
+
+ ready: bool
+ motor_control_mode: MotorControlMode
+ last_alive: float | None
+ control_loop_stats: dict[str, Any]
+ error: str | None = None
diff --git a/reachy_mini/src/reachy_mini/daemon/daemon.py b/reachy_mini/src/reachy_mini/daemon/daemon.py
new file mode 100644
index 0000000000000000000000000000000000000000..96b642657653110187011ad13bf942d752e51e04
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/daemon.py
@@ -0,0 +1,485 @@
+"""Daemon for Reachy Mini robot.
+
+This module provides a daemon that runs a backend for either a simulated Reachy Mini using Mujoco or a real Reachy Mini robot using a serial connection.
+It includes methods to start, stop, and restart the daemon, as well as to check its status.
+It also provides a command-line interface for easy interaction.
+"""
+
+import asyncio
+import json
+import logging
+import time
+from dataclasses import asdict, dataclass
+from enum import Enum
+from importlib.metadata import PackageNotFoundError, version
+from threading import Event, Thread
+from typing import Any, Optional
+
+from reachy_mini.daemon.backend.abstract import MotorControlMode
+from reachy_mini.daemon.utils import (
+ convert_enum_to_dict,
+ find_serial_port,
+ get_ip_address,
+)
+
+from ..io.zenoh_server import ZenohServer
+from .backend.mujoco import MujocoBackend, MujocoBackendStatus
+from .backend.robot import RobotBackend, RobotBackendStatus
+
+
+class Daemon:
+ """Daemon for simulated or real Reachy Mini robot.
+
+ Runs the server with the appropriate backend (Mujoco for simulation or RobotBackend for real hardware).
+ """
+
+ def __init__(self, log_level: str = "INFO", wireless_version: bool = False) -> None:
+ """Initialize the Reachy Mini daemon."""
+ self.log_level = log_level
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(self.log_level)
+
+ self.wireless_version = wireless_version
+
+ self.backend: "RobotBackend | MujocoBackend | None" = None
+ # Get package version
+ try:
+ package_version = version("reachy_mini")
+ self.logger.info(f"Daemon version: {package_version}")
+ except PackageNotFoundError:
+ package_version = None
+ self.logger.warning("Could not determine daemon version")
+
+ self._status = DaemonStatus(
+ state=DaemonState.NOT_INITIALIZED,
+ wireless_version=wireless_version,
+ simulation_enabled=None,
+ backend_status=None,
+ error=None,
+ wlan_ip=None,
+ version=package_version,
+ )
+ self._thread_event_publish_status = Event()
+
+ self._webrtc: Optional[Any] = (
+ None # type GstWebRTC imported for wireless version only
+ )
+ if wireless_version:
+ from reachy_mini.media.webrtc_daemon import GstWebRTC
+
+ self._webrtc = GstWebRTC(log_level)
+
+ async def start(
+ self,
+ sim: bool = False,
+ serialport: str = "auto",
+ scene: str = "empty",
+ localhost_only: bool = True,
+ wake_up_on_start: bool = True,
+ check_collision: bool = False,
+ kinematics_engine: str = "AnalyticalKinematics",
+ headless: bool = False,
+ hardware_config_filepath: str | None = None,
+ ) -> "DaemonState":
+ """Start the Reachy Mini daemon.
+
+ Args:
+ sim (bool): If True, run in simulation mode using Mujoco. Defaults to False.
+ serialport (str): Serial port for real motors. Defaults to "auto", which will try to find the port automatically.
+ scene (str): Name of the scene to load in simulation mode ("empty" or "minimal"). Defaults to "empty".
+ localhost_only (bool): If True, restrict the server to localhost only clients. Defaults to True.
+ wake_up_on_start (bool): If True, wake up Reachy Mini on start. Defaults to True.
+ check_collision (bool): If True, enable collision checking. Defaults to False.
+ kinematics_engine (str): Kinematics engine to use. Defaults to "AnalyticalKinematics".
+ headless (bool): If True, run Mujoco in headless mode (no GUI). Defaults to False.
+ hardware_config_filepath (str | None): Path to the hardware configuration YAML file. Defaults to None.
+
+ Returns:
+ DaemonState: The current state of the daemon after attempting to start it.
+
+ """
+ if self._status.state == DaemonState.RUNNING:
+ self.logger.warning("Daemon is already running.")
+ return self._status.state
+
+ self.logger.info(
+ f"Daemon start parameters: sim={sim}, serialport={serialport}, scene={scene}, localhost_only={localhost_only}, wake_up_on_start={wake_up_on_start}, check_collision={check_collision}, kinematics_engine={kinematics_engine}, headless={headless}, hardware_config_filepath={hardware_config_filepath}"
+ )
+
+ self._status.simulation_enabled = sim
+
+ if not localhost_only:
+ self._status.wlan_ip = get_ip_address()
+
+ self._start_params = {
+ "sim": sim,
+ "serialport": serialport,
+ "headless": headless,
+ "scene": scene,
+ "localhost_only": localhost_only,
+ }
+
+ self.logger.info("Starting Reachy Mini daemon...")
+ self._status.state = DaemonState.STARTING
+
+ try:
+ self.backend = self._setup_backend(
+ wireless_version=self.wireless_version,
+ sim=sim,
+ serialport=serialport,
+ scene=scene,
+ check_collision=check_collision,
+ kinematics_engine=kinematics_engine,
+ headless=headless,
+ hardware_config_filepath=hardware_config_filepath,
+ )
+ except Exception as e:
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+ raise e
+
+ self.server = ZenohServer(self.backend, localhost_only=localhost_only)
+ self.server.start()
+
+ self._thread_publish_status = Thread(target=self._publish_status, daemon=True)
+ self._thread_publish_status.start()
+
+ def backend_wrapped_run() -> None:
+ assert self.backend is not None, (
+ "Backend should be initialized before running."
+ )
+
+ try:
+ self.backend.wrapped_run()
+ except Exception as e:
+ self.logger.error(f"Backend encountered an error: {e}")
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+ self.server.stop()
+ self.backend = None
+
+ self.backend_run_thread = Thread(target=backend_wrapped_run)
+ self.backend_run_thread.start()
+
+ if not self.backend.ready.wait(timeout=2.0):
+ self.logger.error(
+ "Backend is not ready after 2 seconds. Some error occurred."
+ )
+ self._status.state = DaemonState.ERROR
+ self._status.error = self.backend.error
+ return self._status.state
+
+ if wake_up_on_start:
+ try:
+ self.logger.info("Waking up Reachy Mini...")
+ self.backend.set_motor_control_mode(MotorControlMode.Enabled)
+ await self.backend.wake_up()
+ except Exception as e:
+ self.logger.error(f"Error while waking up Reachy Mini: {e}")
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+ return self._status.state
+ except KeyboardInterrupt:
+ self.logger.warning("Wake up interrupted by user.")
+ self._status.state = DaemonState.STOPPING
+ return self._status.state
+
+ if self._webrtc:
+ await asyncio.sleep(
+ 0.2
+ ) # Give some time for the backend to release the audio device
+ self._webrtc.start()
+
+ self.logger.info("Daemon started successfully.")
+ self._status.state = DaemonState.RUNNING
+ return self._status.state
+
+ async def stop(self, goto_sleep_on_stop: bool = True) -> "DaemonState":
+ """Stop the Reachy Mini daemon.
+
+ Args:
+ goto_sleep_on_stop (bool): If True, put Reachy Mini to sleep on stop. Defaults to True.
+
+ Returns:
+ DaemonState: The current state of the daemon after attempting to stop it.
+
+ """
+ if self._status.state == DaemonState.STOPPED:
+ self.logger.warning("Daemon is already stopped.")
+ return self._status.state
+
+ if self.backend is None:
+ self.logger.info("Daemon backend is not initialized.")
+ self._status.state = DaemonState.STOPPED
+ return self._status.state
+
+ try:
+ if self._status.state in (DaemonState.STOPPING, DaemonState.ERROR):
+ goto_sleep_on_stop = False
+
+ self.logger.info("Stopping Reachy Mini daemon...")
+ self._status.state = DaemonState.STOPPING
+ self.backend.is_shutting_down = True
+ self._thread_event_publish_status.set()
+ self.server.stop()
+
+ if self._webrtc:
+ self._webrtc.stop()
+
+ if goto_sleep_on_stop:
+ try:
+ self.logger.info("Putting Reachy Mini to sleep...")
+ self.backend.set_motor_control_mode(MotorControlMode.Enabled)
+ await self.backend.goto_sleep()
+ self.backend.set_motor_control_mode(MotorControlMode.Disabled)
+ except Exception as e:
+ self.logger.error(f"Error while putting Reachy Mini to sleep: {e}")
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+ except KeyboardInterrupt:
+ self.logger.warning("Sleep interrupted by user.")
+ self._status.state = DaemonState.STOPPING
+
+ self.backend.should_stop.set()
+ self.backend_run_thread.join(timeout=5.0)
+ if self.backend_run_thread.is_alive():
+ self.logger.warning("Backend did not stop in time, forcing shutdown.")
+ self._status.state = DaemonState.ERROR
+
+ self.backend.close()
+ self.backend.ready.clear()
+
+ if self._status.state != DaemonState.ERROR:
+ self.logger.info("Daemon stopped successfully.")
+ self._status.state = DaemonState.STOPPED
+ except Exception as e:
+ self.logger.error(f"Error while stopping the daemon: {e}")
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+ except KeyboardInterrupt:
+ self.logger.warning("Daemon already stopping...")
+
+ if self.backend is not None:
+ backend_status = self.backend.get_status()
+ if backend_status.error:
+ self._status.state = DaemonState.ERROR
+
+ self.backend = None
+
+ return self._status.state
+
+ async def restart(
+ self,
+ sim: Optional[bool] = None,
+ serialport: Optional[str] = None,
+ scene: Optional[str] = None,
+ headless: Optional[bool] = None,
+ localhost_only: Optional[bool] = None,
+ wake_up_on_start: Optional[bool] = None,
+ goto_sleep_on_stop: Optional[bool] = None,
+ ) -> "DaemonState":
+ """Restart the Reachy Mini daemon.
+
+ Args:
+ sim (bool): If True, run in simulation mode using Mujoco. Defaults to None (uses the previous value).
+ serialport (str): Serial port for real motors. Defaults to None (uses the previous value).
+ scene (str): Name of the scene to load in simulation mode ("empty" or "minimal"). Defaults to None (uses the previous value).
+ headless (bool): If True, run Mujoco in headless mode (no GUI). Defaults to None (uses the previous value).
+ localhost_only (bool): If True, restrict the server to localhost only clients. Defaults to None (uses the previous value).
+ wake_up_on_start (bool): If True, wake up Reachy Mini on start. Defaults to None (don't wake up).
+ goto_sleep_on_stop (bool): If True, put Reachy Mini to sleep on stop. Defaults to None (don't go to sleep).
+
+ Returns:
+ DaemonState: The current state of the daemon after attempting to restart it.
+
+ """
+ if self._status.state == DaemonState.STOPPED:
+ self.logger.warning("Daemon is not running.")
+ return self._status.state
+
+ if self._status.state in (DaemonState.RUNNING, DaemonState.ERROR):
+ self.logger.info("Restarting Reachy Mini daemon...")
+
+ await self.stop(
+ goto_sleep_on_stop=goto_sleep_on_stop
+ if goto_sleep_on_stop is not None
+ else False
+ )
+ params: dict[str, Any] = {
+ "sim": sim if sim is not None else self._start_params["sim"],
+ "serialport": serialport
+ if serialport is not None
+ else self._start_params["serialport"],
+ "scene": scene if scene is not None else self._start_params["scene"],
+ "headless": headless
+ if headless is not None
+ else self._start_params["headless"],
+ "localhost_only": localhost_only
+ if localhost_only is not None
+ else self._start_params["localhost_only"],
+ "wake_up_on_start": wake_up_on_start
+ if wake_up_on_start is not None
+ else False,
+ }
+
+ return await self.start(**params)
+
+ raise NotImplementedError(
+ "Restarting is only supported when the daemon is in RUNNING or ERROR state."
+ )
+
+ def status(self) -> "DaemonStatus":
+ """Get the current status of the Reachy Mini daemon."""
+ if self.backend is not None:
+ self._status.backend_status = self.backend.get_status()
+
+ assert self._status.backend_status is not None, (
+ "Backend status should not be None after backend initialization."
+ )
+
+ if self._status.backend_status.error:
+ self._status.state = DaemonState.ERROR
+ self._status.error = self._status.backend_status.error
+ else:
+ self._status.backend_status = None
+
+ return self._status
+
+ def _publish_status(self) -> None:
+ self._thread_event_publish_status.clear()
+ while self._thread_event_publish_status.is_set() is False:
+ json_str = json.dumps(
+ asdict(self.status(), dict_factory=convert_enum_to_dict)
+ )
+ self.server.pub_status.put(json_str)
+ time.sleep(1)
+
+ async def run4ever(
+ self,
+ sim: bool = False,
+ serialport: str = "auto",
+ scene: str = "empty",
+ localhost_only: bool = True,
+ wake_up_on_start: bool = True,
+ goto_sleep_on_stop: bool = True,
+ check_collision: bool = False,
+ kinematics_engine: str = "AnalyticalKinematics",
+ headless: bool = False,
+ ) -> None:
+ """Run the Reachy Mini daemon indefinitely.
+
+ First, it starts the daemon, then it keeps checking the status and allows for graceful shutdown on user interrupt (Ctrl+C).
+
+ Args:
+ sim (bool): If True, run in simulation mode using Mujoco. Defaults to False.
+ serialport (str): Serial port for real motors. Defaults to "auto", which will try to find the port automatically.
+ scene (str): Name of the scene to load in simulation mode ("empty" or "minimal"). Defaults to "empty".
+ localhost_only (bool): If True, restrict the server to localhost only clients. Defaults to True.
+ wake_up_on_start (bool): If True, wake up Reachy Mini on start. Defaults to True.
+ goto_sleep_on_stop (bool): If True, put Reachy Mini to sleep on stop. Defaults to True
+ check_collision (bool): If True, enable collision checking. Defaults to False.
+ kinematics_engine (str): Kinematics engine to use. Defaults to "AnalyticalKinematics".
+ headless (bool): If True, run Mujoco in headless mode (no GUI). Defaults to False.
+
+ """
+ await self.start(
+ sim=sim,
+ serialport=serialport,
+ scene=scene,
+ localhost_only=localhost_only,
+ wake_up_on_start=wake_up_on_start,
+ check_collision=check_collision,
+ kinematics_engine=kinematics_engine,
+ headless=headless,
+ )
+
+ if self._status.state == DaemonState.RUNNING:
+ try:
+ self.logger.info("Daemon is running. Press Ctrl+C to stop.")
+ while self.backend_run_thread.is_alive():
+ self.logger.info(f"Daemon status: {self.status()}")
+ for _ in range(10):
+ self.backend_run_thread.join(timeout=1.0)
+ else:
+ self.logger.error("Backend thread has stopped unexpectedly.")
+ self._status.state = DaemonState.ERROR
+ except KeyboardInterrupt:
+ self.logger.warning("Daemon interrupted by user.")
+ except Exception as e:
+ self.logger.error(f"An error occurred: {e}")
+ self._status.state = DaemonState.ERROR
+ self._status.error = str(e)
+
+ await self.stop(goto_sleep_on_stop)
+
+ def _setup_backend(
+ self,
+ wireless_version: bool,
+ sim: bool,
+ serialport: str,
+ scene: str,
+ check_collision: bool,
+ kinematics_engine: str,
+ headless: bool,
+ hardware_config_filepath: str | None = None,
+ ) -> "RobotBackend | MujocoBackend":
+ if sim:
+ return MujocoBackend(
+ scene=scene,
+ check_collision=check_collision,
+ kinematics_engine=kinematics_engine,
+ headless=headless,
+ )
+ else:
+ if serialport == "auto":
+ ports = find_serial_port(wireless_version=wireless_version)
+
+ if len(ports) == 0:
+ raise RuntimeError(
+ "No Reachy Mini serial port found. "
+ "Check USB connection and permissions. "
+ "Or directly specify the serial port using --serialport."
+ )
+ elif len(ports) > 1:
+ raise RuntimeError(
+ f"Multiple Reachy Mini serial ports found {ports}."
+ "Please specify the serial port using --serialport."
+ )
+
+ serialport = ports[0]
+ self.logger.info(f"Found Reachy Mini serial port: {serialport}")
+
+ self.logger.info(
+ f"Creating RobotBackend with parameters: serialport={serialport}, check_collision={check_collision}, kinematics_engine={kinematics_engine}"
+ )
+ return RobotBackend(
+ serialport=serialport,
+ log_level=self.log_level,
+ check_collision=check_collision,
+ kinematics_engine=kinematics_engine,
+ hardware_config_filepath=hardware_config_filepath,
+ )
+
+
+class DaemonState(Enum):
+ """Enum representing the state of the Reachy Mini daemon."""
+
+ NOT_INITIALIZED = "not_initialized"
+ STARTING = "starting"
+ RUNNING = "running"
+ STOPPING = "stopping"
+ STOPPED = "stopped"
+ ERROR = "error"
+
+
+@dataclass
+class DaemonStatus:
+ """Dataclass representing the status of the Reachy Mini daemon."""
+
+ state: DaemonState
+ wireless_version: bool
+ simulation_enabled: Optional[bool]
+ backend_status: Optional[RobotBackendStatus | MujocoBackendStatus]
+ error: Optional[str] = None
+ wlan_ip: Optional[str] = None
+ version: Optional[str] = None
diff --git a/reachy_mini/src/reachy_mini/daemon/utils.py b/reachy_mini/src/reachy_mini/daemon/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..56230067d1b324d76587e4712160b2f8b6337631
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/daemon/utils.py
@@ -0,0 +1,115 @@
+"""Utilities for managing the Reachy Mini daemon."""
+
+import os
+import socket
+import struct
+import subprocess
+import time
+from enum import Enum
+from typing import Any, List
+
+import psutil
+import serial.tools.list_ports
+
+
+def daemon_check(spawn_daemon: bool, use_sim: bool) -> None:
+ """Check if the Reachy Mini daemon is running and spawn it if necessary."""
+
+ def is_python_script_running(
+ script_name: str,
+ ) -> tuple[bool, int | None, bool | None]:
+ """Check if a specific Python script is running."""
+ found_script = False
+ simluation_enabled = False
+ for proc in psutil.process_iter(["pid", "name", "cmdline"]):
+ try:
+ for cmd in proc.info["cmdline"]:
+ if script_name in cmd:
+ found_script = True
+ if "--sim" in cmd:
+ simluation_enabled = True
+ if found_script:
+ return True, proc.pid, simluation_enabled
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
+ continue
+ return False, None, None
+
+ if spawn_daemon:
+ daemon_is_running, pid, sim = is_python_script_running("reachy-mini-daemon")
+ if daemon_is_running and sim == use_sim:
+ print(
+ f"Reachy Mini daemon is already running (PID: {pid}). "
+ "No need to spawn a new one."
+ )
+ return
+ elif daemon_is_running and sim != use_sim:
+ print(
+ f"Reachy Mini daemon is already running (PID: {pid}) with a different configuration. "
+ )
+ print("Killing the existing daemon...")
+ assert pid is not None, "PID should not be None if daemon is running"
+ os.kill(pid, 9)
+ time.sleep(1)
+
+ print("Starting a new daemon...")
+ subprocess.Popen(
+ ["reachy-mini-daemon", "--sim"] if use_sim else ["reachy-mini-daemon"],
+ start_new_session=True,
+ )
+
+
+def find_serial_port(
+ wireless_version: bool = False,
+ vid: str = "1a86",
+ pid: str = "55d3",
+ pi_uart: str = "/dev/ttyAMA3",
+) -> list[str]:
+ """Find the serial port for Reachy Mini based on VID and PID or the Raspberry Pi UART for the wireless version.
+
+ Args:
+ wireless_version (bool): Whether to look for the wireless version using the Raspberry Pi UART.
+ vid (str): Vendor ID of the device. (eg. "1a86").
+ pid (str): Product ID of the device. (eg. "55d3").
+ pi_uart (str): Path to the Raspberry Pi UART device. (eg. "/dev/ttyAMA3").
+
+ """
+ # If it's a wireless version, we should use the Raspberry Pi UART
+ if wireless_version:
+ return [pi_uart] if os.path.exists(pi_uart) else []
+
+ # If it's a lite version, we should find it using the VID and PID
+ ports = serial.tools.list_ports.comports()
+
+ vid = vid.upper()
+ pid = pid.upper()
+
+ return [p.device for p in ports if f"USB VID:PID={vid}:{pid}" in p.hwid]
+
+
+def get_ip_address(ifname: str = "wlan0") -> str | None:
+ """Get the IP address of a specific network interface (Linux Only)."""
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ import fcntl
+
+ return socket.inet_ntoa(
+ fcntl.ioctl(
+ s.fileno(),
+ 0x8915, # SIOCGIFADDR
+ struct.pack("256s", ifname[:15].encode("utf-8")),
+ )[20:24]
+ )
+ except OSError:
+ print(f"Could not get IP address for interface {ifname}.")
+ return None
+
+
+def convert_enum_to_dict(data: List[Any]) -> dict[str, Any]:
+ """Convert a dataclass containing Enums to a dictionary with enum values."""
+
+ def convert_value(obj: Any) -> Any:
+ if isinstance(obj, Enum):
+ return obj.value
+ return obj
+
+ return dict((k, convert_value(v)) for k, v in data)
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part
new file mode 100644
index 0000000000000000000000000000000000000000..323829c404dac84610e29fae4f4079c973f4630e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "48f0e86376312f8ca242a082",
+ "fullConfiguration": "default",
+ "id": "MUIOPFRAXdyRYA5/y",
+ "isStandardContent": false,
+ "name": "5W_SPEAKER <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl
new file mode 100644
index 0000000000000000000000000000000000000000..00a29236bdb8101ddee079de562870f3a5877e12
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:89c220ac5fa6dd559fcd2f3faccb7197904ab6b882ccf5d2ad04b17a09d58b98
+size 229884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part
new file mode 100644
index 0000000000000000000000000000000000000000..3e6670e8d45549d1de88eff9330188d29998fd6f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "d5df0bcac25cddb763b3a090",
+ "fullConfiguration": "default",
+ "id": "MT+KnYpqaZ58+3raS",
+ "isStandardContent": false,
+ "name": "antenna <2>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl
new file mode 100644
index 0000000000000000000000000000000000000000..1dee4a7c0fd16b225385d5ef7866a802ea4ee18e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ebe995bfc23aa8f832e86065c364ddf5bf0c1341d6cd4da30790631c7e15a275
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..7b7ceb2108438e03d3e23bb4fd7e6bd75ffc9b68
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "M6OWFp5hfkfES9FmB",
+ "isStandardContent": false,
+ "name": "Antenna_body_3DPrint <2>",
+ "partId": "JF/",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..57603870b064c815155ec0a14b722b374291a492
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f760ace60b1a368ba7c51bcd01a152a85e66a587fb24d55886cb9d9ffb9008f
+size 241784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..01fa39d2ec059631dd9c404a337f35c189a554c1
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MHVB1f7lyvjCWrFJe",
+ "isStandardContent": false,
+ "name": "Antenna_Holder-L_3DPrint <1>",
+ "partId": "KFDB",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..661802d1420a882c69b935142c8bd2e6ad5b475b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2163af81f112d0e1503f284213e831ff7692960cbcb7085ad6db8b63912fd466
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..047b5202be64618854767faa97c9547622e8e4af
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MX6Uc6wQ4cFgySRks",
+ "isStandardContent": false,
+ "name": "Antenna_Holder-R_3DPrint <1>",
+ "partId": "JFv",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2a3548a26c818890c82527ed4fa6f1a448678e4e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f516056c535ac6bc8ec94983e48945bfca75ccf17d058263bfe67c4883d8932
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..49e9dfb4a2f1f6ae62ded9bad7e1e89dafa13d46
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MObKCzX1Fco0wvr0/",
+ "isStandardContent": false,
+ "name": "Antenna_interface_3DPrint <2>",
+ "partId": "JF7",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..15b32f15011a3154fc5c832e7573d6dfac98b538
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:662384ed2de2c37fff6d5bab1ba0ccb76ee03f5d1680043d255e33e879b481bb
+size 179984
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part
new file mode 100644
index 0000000000000000000000000000000000000000..6268a27d56b5bc11c338dd4130330ea77e290c13
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "ae2c63bc6f578a46d35cd365",
+ "fullConfiguration": "default",
+ "id": "MDupaBfND/yK9FERm",
+ "isStandardContent": false,
+ "name": "Arducam <1>",
+ "partId": "JGD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl
new file mode 100644
index 0000000000000000000000000000000000000000..7badfd2fe38b5864a648f7736f64e70888077a9a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8916e1a5fe5ffd8c0c3eded5f173871be269a2c26a437db7f7039e6c8cc7a020
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part
new file mode 100644
index 0000000000000000000000000000000000000000..521486400deaaa9c9cbd828574baa9dcb29af98d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MHbJVGQ/2CguoKbZz",
+ "isStandardContent": false,
+ "name": "Arm <1>",
+ "partId": "JsD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl
new file mode 100644
index 0000000000000000000000000000000000000000..b104f0ad630bec3de1516e6148b4bde7ec9c7e60
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1466fc2854bf4d3eb92b5f5f595e2c1a1dd90f8f9bbdcc581f88c541e72a47f
+size 104784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part
new file mode 100644
index 0000000000000000000000000000000000000000..afa468c7e48ae9fbd21eab01a9541462794d9df4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "Md1pPF2974d9JOL2l",
+ "isStandardContent": false,
+ "name": "B3B-EH <1>",
+ "partId": "JFr",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl
new file mode 100644
index 0000000000000000000000000000000000000000..29565d9e384e88ae8aaaa9b10ff84fb12f89ba09
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:388ed3ff01359dac77cad8f1a14c4babeb4e2b72b5c1c60333fa2682bf62ac17
+size 9884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part
new file mode 100644
index 0000000000000000000000000000000000000000..a30748b802c7941da860393d9a0dc99cf1297c09
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MMuPyh0yjSzJko9Po",
+ "isStandardContent": false,
+ "name": "B3B-EH_1 <1>",
+ "partId": "JFT",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..268d4d96bd27d4134a27b0115589becd4a6e444b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a741e87c71a9ff662a24ab5d6c7b8397afc6a7f7506910992814506d98d8e21f
+size 9884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part
new file mode 100644
index 0000000000000000000000000000000000000000..cdeff374fef867c1c0be03f0c6b79ccead7fb1d3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "0aa833fd983f27ea7fb43256",
+ "fullConfiguration": "default",
+ "id": "Msm1EC3X/D1/s/Uus",
+ "isStandardContent": false,
+ "name": "Ball <13>",
+ "partId": "JSD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl
new file mode 100644
index 0000000000000000000000000000000000000000..6261467e2f5ecd06c86600bdf01d22a1b4d70cab
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa04fda74ce21c562e6ad5645c619c425ce2644f29463cef38ad791c3e98b1c7
+size 158484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part
new file mode 100644
index 0000000000000000000000000000000000000000..c80cc1b7772338e12004198fb2e655376f7c45ac
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "d5ff0fe74f14f26be2deb5d4",
+ "fullConfiguration": "default",
+ "id": "MYk5LvI8gIvlMtfS7",
+ "isStandardContent": false,
+ "name": "Bearing_85x110x13 <1>",
+ "partId": "JGD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl
new file mode 100644
index 0000000000000000000000000000000000000000..751ac994ab783ba08d731006ca50d2c789252307
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d6052d367e0a9f36063a3be16c77e34d8c5da3bb001346a317b75f19b9e60fe
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part
new file mode 100644
index 0000000000000000000000000000000000000000..b5f2505e96232073a4b652b2eb2eddaa87272477
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "MpioSzVOUQtkaeTVH",
+ "isStandardContent": false,
+ "name": "big_lens <1>",
+ "partId": "J0D",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl
new file mode 100644
index 0000000000000000000000000000000000000000..5528ca0d5bf74e8e3d48709d244d9187ab36aadf
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:28b05e56f4c9fcf6458ef7ae0a49b016251afbb463199095922eaac0d597bbe5
+size 266484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part
new file mode 100644
index 0000000000000000000000000000000000000000..d866fd19907b81ae2c610e1d13939d956425f95e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "f67f6ec9755019fa8d4cde38",
+ "fullConfiguration": "default",
+ "id": "M6vM68+58boAYt2AP",
+ "isStandardContent": false,
+ "name": "Big Lens D40 <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl
new file mode 100644
index 0000000000000000000000000000000000000000..afa5536066cdf16c9d355babcf7782f4a2a4a54b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4df711c8e29a677f62622d57c5cac01257ae9a44f46adb67fefb54b8fe52a6eb
+size 151284
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..a35e3a2946f5fc2046131caa52616c4a53643654
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "M40W4a7sQuI8QEDbK",
+ "isStandardContent": false,
+ "name": "BODY-DOWN_3DPrint <1>",
+ "partId": "JFP",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..172d7baaebbd65b5cc0ca39e8f3a467201fde9e1
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b3d46f953d49182f85fa818a5d3935c688afffdf4d7a0119aa58f9292a8e3707
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..07ac3aaa9b1993246e54ba9b5cdc8ac3ceb77db2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MFPx9mgqcSNR4iAYO",
+ "isStandardContent": false,
+ "name": "BODY-FOOT_3DPrint <1>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..5f68209f5f49b37804f6f6bec23a243e5c4ece24
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eab9440d4d6745c99445dba3887f4668b48780e8a10aa1efc05e3f9481b40caf
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..17722be87830aed010dab466fc4555df2ba6847c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MfqCGZgMWtuWpureN",
+ "isStandardContent": false,
+ "name": "BODY_TOP_3DPrint <1>",
+ "partId": "JFb",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..067b40f1f47f0bc3e905e17de3f529f3f3e4a6a3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b985a9288abfc9894335c609bf24943551ae185e855f40c9a9f89c6a7c0eb831
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..a967bb56a6d35d19dd106c2ecd4a0520416444a8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MttvYsoT0rlrLKDwS",
+ "isStandardContent": false,
+ "name": "BODY-TURNING_3DPrint <1>",
+ "partId": "JFL",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..0cd5f031f9086ea2d9661fb89e2e3a4064a279d3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c9757573938050cb1c0a4c7fdf37d8bbb3f9cf28dc19444c3487c1c5765d264
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part
new file mode 100644
index 0000000000000000000000000000000000000000..001ff6567e8a116aafd456c3ecc575d61718947a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MgqLgbONW+9OAHLGG",
+ "isStandardContent": false,
+ "name": "bottom_body <1>",
+ "partId": "RcDD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl
new file mode 100644
index 0000000000000000000000000000000000000000..32350dd01a46e79c514d71bc8828e84f6729e3ed
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c7b36963b7ae1cf06cc50aba61d5024df7f006274006671f378a0a52e29065eb
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part
new file mode 100644
index 0000000000000000000000000000000000000000..afd31549097f79cdc80dd406346fa098fd3d5720
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MHzA6RZ/kEAMV93SN",
+ "isStandardContent": false,
+ "name": "BTS2_M2_6X8 <1>",
+ "partId": "JFL",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl
new file mode 100644
index 0000000000000000000000000000000000000000..c9d6877ca329f3ed698cc3fbc48bda60958d1304
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:303a706276ef0a3dcf8b6deeabf73969d2c48cb6628209bf0fed825b622deccb
+size 269184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m
new file mode 100644
index 0000000000000000000000000000000000000000..edda0bc7176b6fcd4c13c398aa0216c36eaa9aaf
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m
@@ -0,0 +1 @@
+Created with: https://github.com/pollen-robotics/reachy_mini_stl_convexify
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..b134982cae9842ee326de029634c65b0f1890489
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8f92539090c8fbef3a292ffbdc6a1019828ad5e33a5b87354ad08032a59d0c35
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..dc98ea93cd56cbf43dc023e4f3df943f20c2220d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9186fdc67813c3b7f3fba721cbfbb14d5e6f20bcabe1897d488913b6879bfd78
+size 4784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..592ecb1efa1423f521ef6f4ba8202982f42a1c5d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2db1d2726afd373e145a7b3cb8340983eeff49c07f8e4057e2d4eec7f91c35df
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2bea29e74aa6e2c47e3e59f8fadd02cb9f36f1b5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3e7916bfcdccd254c51143774575c7a0924a1a1939fd9a4755ae3b8f2c920bdc
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..728659d336df95aa543bf2e93753747d4aa6ed88
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:94d9c386d10344ae4fa03baae3ea0ca935c8ab2a5d41352a4b4c59c3ac2fd9e1
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..d296a17e93a2f2ecc90374dd69931f453c29d241
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e893cc8cd9581722ffe7798031076190cdc2264edc70694d925be5cf326b8bc2
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..d990c5488dd499dd50f333409209da943c591a43
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ec16d901b544cd898eb1836d4f61fc16855919bd856c82f0fd23ac180e684af
+size 4884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..b5a036399c116ef5cf2413cecff5c2734e368e36
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9ba0e5a4dc95869e16212e69fa3712ea7f021378cf76db0b1a8d2300eaab3c64
+size 6884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..6e405fc6959d1daf7a57d2c089733783dcf06e0f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c22bb171ab47155a4fe2946cd267f8e43cba901dd11d3106082bbe1848f90b4
+size 190084
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..34019f999ae4a5fa23f8041ce2759f927def44c8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9a2c6189ab48a1b296ab5e0695363ab20021154dc7823d3eef8a188b992c1682
+size 195784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..67603dad86fad7502cff1fe8bac2023d0263570c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cbdf0d34da912a29174148bd78b90e3541755f9bc20d2a5cc624e3565aef3d94
+size 78684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl
new file mode 100644
index 0000000000000000000000000000000000000000..37d6256085c7585cef1e375b23450dee51b551b0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e7c26eb2033477e711d6ea535b955a91f443b74d21a4d8e5ed1bfb72c10c401
+size 245084
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..f0f3c8dae35837c47e7c630011c89b8330b6ef90
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:27dcf3bcfc55a11f5fe3a6a5f01fe253fb5e9cf9d736aa9b83a44d286aeef38c
+size 185384
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..ce58be8bce5e0e9b3a9b5c91d76167ce12e4a511
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:89940a91f8e1bf1d1b7c4844bc58a8a03f5c3983cfc174573e8f60f6a47c1109
+size 182484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..8964aa9383d3a6f3c59f79527b4b9959ea4e8cc5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ccf48cd480051da28caee941ad1e130f74cebfd2c474d5791d5e014e10fcdfd
+size 219184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl
new file mode 100644
index 0000000000000000000000000000000000000000..1d636e5fc50d5c6b06349b81a41b6c60c2857ac4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5a28add53d3bc49c5b7f6d9e8f8f0884bbfa1ad9fe81af5ca58f01d08a072ed9
+size 1359584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part
new file mode 100644
index 0000000000000000000000000000000000000000..420f5df50f4a772a4de847dc44aff9acc747e308
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "M0s224h4t0HqzV/u4",
+ "isStandardContent": false,
+ "name": "DC15_A01_CASE_B_DUMMY <1>",
+ "partId": "JFf",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl
new file mode 100644
index 0000000000000000000000000000000000000000..be16e3d867aeec0cd64d95ff10324f7b57d0da17
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b108f9390168a9052b408b292e316112f28e11d5ca51e28a374f29ce9ef2790
+size 248884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part
new file mode 100644
index 0000000000000000000000000000000000000000..95fecb125aa4742c5d8b229958d10121baa801bc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MJyRwhvvOMia59Q6o",
+ "isStandardContent": false,
+ "name": "DC15_A01_CASE_F_DUMMY <1>",
+ "partId": "JFj",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl
new file mode 100644
index 0000000000000000000000000000000000000000..366b36df275c4b23ffa21d18f7d21e3364059056
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6de99b6af2361d2fdb751bcb78768e7565bc5b40fbdd6cda3e2c92f277135d33
+size 314484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part
new file mode 100644
index 0000000000000000000000000000000000000000..c3f473599d4d0994ac8386e21336af3d76a7e60b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "M6qXbiravwBh/M2FZ",
+ "isStandardContent": false,
+ "name": "DC15_A01_CASE_M_DUMMY <1>",
+ "partId": "JFn",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl
new file mode 100644
index 0000000000000000000000000000000000000000..409e7a8400b24fa00e323e2c9f73c22d78129a95
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:716c7f05cb780b5eb1dc7e614b8376e587683f2922fcb386d0b8152a73b5af68
+size 162184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part
new file mode 100644
index 0000000000000000000000000000000000000000..5e29396352f910c9971633c0a317ebdaa3c1b4f3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "M/hbODfuW0gbGU1TK",
+ "isStandardContent": false,
+ "name": "DC15_A01_HORN_DUMMY <9>",
+ "partId": "JFX",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl
new file mode 100644
index 0000000000000000000000000000000000000000..bee160568e7c9b9c01f7632a6807a5fd8aee982d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1fb77ee29fb48c09c125ae3a3cd4641673a08f513d3a5002f1453580a41c4cb
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part
new file mode 100644
index 0000000000000000000000000000000000000000..76e2a26f2606b2474ade0b1c632c6fb11e02fd4f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MFvbXu4svYiIz5iLO",
+ "isStandardContent": false,
+ "name": "DC15_A01_LED_CAP2_DUMMY <1>",
+ "partId": "JFv",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl
new file mode 100644
index 0000000000000000000000000000000000000000..b8ff0e55b8b8160e539b5756e3f6bd82e62be0e9
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c8ea014b723e253c28fa3bf5062c794b18470cd7b5d6b33e723a9638d974f25
+size 1684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part
new file mode 100644
index 0000000000000000000000000000000000000000..c1c9d50416f6a11d24ed211237730d83d5bfb574
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Default",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "9cbc0f0673222989756dc3bd",
+ "documentVersion": "ab0d611cff58eb03c4b43922",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Default",
+ "id": "MQEuvqp2Q/VgvHc2b",
+ "isStandardContent": false,
+ "name": "drive_palonier <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl
new file mode 100644
index 0000000000000000000000000000000000000000..655a93f74785a9815052b77e84598c7c67e47965
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:161932a6a7d146aaa0e1c469154f8d9ee40bca2ba32356c1646f67a2ec47bcd7
+size 102584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part
new file mode 100644
index 0000000000000000000000000000000000000000..fd4edf4fc3970c295370cdb595e0ba27176e6bff
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "562fbad41f5c94b4e8929753",
+ "documentVersion": "b2b50e6fca74f541285598d2",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "id": "MBlf2CAGFrM+vvrAc",
+ "isStandardContent": false,
+ "name": "drive_palonier <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl
new file mode 100644
index 0000000000000000000000000000000000000000..655a93f74785a9815052b77e84598c7c67e47965
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:161932a6a7d146aaa0e1c469154f8d9ee40bca2ba32356c1646f67a2ec47bcd7
+size 102584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part
new file mode 100644
index 0000000000000000000000000000000000000000..12d313dd432d12d02611c71afb432833cc8292b8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "MiwRHVEzZiAIrPHOH",
+ "isStandardContent": false,
+ "name": "eye_support <1>",
+ "partId": "JXD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2e3afdd84ad308bfa756e466dfd04145b0fbb220
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:532e98b07d4b276853ce92806e65f9c4a3fbe2d792e962316f2ed60f687417f0
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part
new file mode 100644
index 0000000000000000000000000000000000000000..79308404fa471c38713cbb0c878c2665d1069fa6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MtAQxXNWlY7iX7UeA",
+ "isStandardContent": false,
+ "name": "Foot <1>",
+ "partId": "RICD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl
new file mode 100644
index 0000000000000000000000000000000000000000..cec7d18f440553e24839cb52eeff292455594487
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:764a08b710160df9e80eb56cde1a68d93627e776a504e8089b8bbc4e46834c9d
+size 104784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..a5ca077d93b7b4394c1571a63d602b3cc8fdbb53
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MsCmmfoZrFsTVz4Vs",
+ "isStandardContent": false,
+ "name": "GLASSES-DOLDER_3DPrint <1>",
+ "partId": "JFf",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..66835911c2bc4fb5718399ca3faca5965ac7c845
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77786d52ead05d3f8c181d0b1bfbe3eaa8f42e0ae81e31847129bb031cbfe6c5
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..d48bd915e6455adc54a9530abcc8c82c94fcd90f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MDdoiC6yCX9ILARl+",
+ "isStandardContent": false,
+ "name": "HEAD-BACK_3DPrint <1>",
+ "partId": "JFr",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..fac35aaa3fa9f4797eee7212959b4a4443ad9897
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e6e60fd980a2a48209471a0ddaac905b5d95c7081646e29c5af15cf3f1a681f
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..e23c54fe13c525a19ddd0f292445b8d6912babf4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MsTE6+BbSl0w4pt9R",
+ "isStandardContent": false,
+ "name": "HEAD-FRONT_3DPrint <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2189569d0796578b42bab48195aa246933950ee6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3474bd1025c6b037e42d6d51428a1370ef88674b9a3988cd6b34e0be0d0049c2
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part
new file mode 100644
index 0000000000000000000000000000000000000000..aed7780b5abda9ed7923ab691da9a6036f6c88e2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "M9OOCqJWvxnZHaK2L",
+ "isStandardContent": false,
+ "name": "head_head_back <1>",
+ "partId": "JsH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl
new file mode 100644
index 0000000000000000000000000000000000000000..7acf9ec2f4fc6695531c8ce3020ea7e1a95a89b6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:106ab6a5983028815da362f680c050606277704e167c1840939ba69af2817fa7
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part
new file mode 100644
index 0000000000000000000000000000000000000000..77a97bea047d5c778a67fb2d3f26d05a0ad83e8e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "Men72YbmKf99vvpY3",
+ "isStandardContent": false,
+ "name": "head_interface <1>",
+ "partId": "R/ED",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl
new file mode 100644
index 0000000000000000000000000000000000000000..c7afd7b911673120cd44c9ee77c6ad7ffd592286
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:463bfc9c01aa004dd4519919c315d06bb2eff75e54fe47ba93d30cc61637a548
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..29d998933e499638ac6e39bd0ecfb84dd411ee45
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MuZC9z8GQmMdOw95U",
+ "isStandardContent": false,
+ "name": "HEAD-MIC_3DPrint <1>",
+ "partId": "JFj",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..1c74e83391471f48e040a9b54a115aaced5b3f6b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9ea18530c4b9305199577d5301583e73f8c8f42694c6e8c50c8103f147da1be8
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part
new file mode 100644
index 0000000000000000000000000000000000000000..fbf68c3c2fddb5fd97aaaa16de52890fc18967ab
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "M326QNbEXm/j3BFCs",
+ "isStandardContent": false,
+ "name": "head_shell_front <1>",
+ "partId": "JsD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl
new file mode 100644
index 0000000000000000000000000000000000000000..765e9cb34ee98f6cd600fbc13e2fff11505f4ecc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d99b397fea112fc6893f5ae4b49fa886595c692e61d533b80eddf40de7bfc477
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..0c203f785318385d2e482018b335c2cf12c8aa1e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MJpFg+P4UMBokBVex",
+ "isStandardContent": false,
+ "name": "LENS-CAP-D30_3DPrint <1>",
+ "partId": "JFn",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..bc190353185d9502e6f671a5cda9e0c58032d572
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:564787bc9073bc68c1598b4674ce751e7a7ed883d305a5cb947d3398360d3737
+size 94284
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..072d2e7861c20c77d66616b95c2be13376a18b41
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "MVC/sOmzNQLy4mD6b",
+ "isStandardContent": false,
+ "name": "LENS-CAP-D40_3DPrint <1>",
+ "partId": "JF3",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..d7cb1d4d52fb366632e58609b8b97eca254c81cb
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5a6364fba94a5a82d8a6512a7e042ccc78a0766756b190af055b0f08ef56471
+size 93684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part
new file mode 100644
index 0000000000000000000000000000000000000000..0b8ca9e141c664383be1c7b4e5738027600456a5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "05dc7ef344fe4c18be495b6e",
+ "fullConfiguration": "default",
+ "id": "MvKFrBJz+CmxF4Ta4",
+ "isStandardContent": false,
+ "name": "M12 fisheye lens 1.8mm <2>",
+ "partId": "JGD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl
new file mode 100644
index 0000000000000000000000000000000000000000..4ee95bc2115804d7426f64d015b8ab1eeb86858a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf2e973bd164cd66b747f16e0ad6c4aafc9492e0b053fd40c283f4beba1b6015
+size 259284
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part
new file mode 100644
index 0000000000000000000000000000000000000000..eb39b918145dc9a79ff57c92655dbcf582e511a3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "M8ZONSAd4C6sMYKK/",
+ "isStandardContent": false,
+ "name": "M12_lens <1>",
+ "partId": "RZBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl
new file mode 100644
index 0000000000000000000000000000000000000000..dd67d13ea74c44329f3c19fb277741bd9df709f8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0225297313cb77de3d8d0efc2b83499fdab9448e479174a5ca83bef24479b85
+size 93684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..37ecea4cb6d1ce978b06ac40a6b22c4d9267f05b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "M0rtL1h+tUfVmZG6t",
+ "isStandardContent": false,
+ "name": "Main Plate <1>",
+ "partId": "RjBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..648c6df7f68bcdc1bf0910ad51097a899575d9b2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a900cdb05046319e1d6821350c785e2bc5a35f83a5392a01876b4cc90a9279eb
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..77cba6ea341cf82f6c09a7b04cdaf81e9eca5c10
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "Mxq3x/8HYdblICsdh",
+ "isStandardContent": false,
+ "name": "Mid Plate <1>",
+ "partId": "R9BD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..17bcdb034acff48e5028b069e83e0f36a4dd4134
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf5afed640fabcd2093f495a1a5d2c71c421b253fb1d22a282d38b866ed3d0c6
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part
new file mode 100644
index 0000000000000000000000000000000000000000..ac721a20bbe4d363d9a76aaf7fa61f135a0cc482
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "Mw5N9cfkbCC/CpCKR",
+ "isStandardContent": false,
+ "name": "MP01062_STEWART_ARM_3 <1>",
+ "partId": "KFHB",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl
new file mode 100644
index 0000000000000000000000000000000000000000..4fd08b227327003a45bf104d95377eb3c24602ac
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7b64f2de2a35b6feb9ed8ef728219f1a9600dfe6fcb70256a2e555cd6c49401e
+size 155584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..16ce78dc2c61e1f70c7edeeb2a85841d152b7f98
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "M7r6qwCMs4B7OGi6t",
+ "isStandardContent": false,
+ "name": "NECK_REFERENCE_3DPrint <1>",
+ "partId": "JFz",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..fb2153b59a4f2e6b86642618500f39632333e233
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8de811dedf772b757b9b414f3af15154c97267050a84c21cd6be3e355fc23eb8
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part
new file mode 100644
index 0000000000000000000000000000000000000000..4034f3f6cad83d93c618705bbe191216bfd55432
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MydIXO1yH59xsOSOQ",
+ "isStandardContent": false,
+ "name": "PHS_1_7X20_5_DC10 <1>",
+ "partId": "JFP",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl
new file mode 100644
index 0000000000000000000000000000000000000000..a0f51417e2d4e782a557a9d604bb4698a14e129b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c3e9d4bddfe4a99c206feba0fe648c1e5caf0aefca6347c7a91ff8f425b58d35
+size 253184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part
new file mode 100644
index 0000000000000000000000000000000000000000..04f5f1900e08385acbda7b363535343d9c0fb927
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MyXqeYka8V5tcbv+v",
+ "isStandardContent": false,
+ "name": "PHS_1_7X20_5_DC10_1 <1>",
+ "partId": "JFb",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl
new file mode 100644
index 0000000000000000000000000000000000000000..f58ca0991a755c8e6960a392c0c5dcf151207344
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9cd84a44f18b39aae56220bfcdfdbf8e39a809c57e4d07c3d7a718c7ab5669e
+size 253184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part
new file mode 100644
index 0000000000000000000000000000000000000000..c4fbc1e88f68eab945d05ad4fc6334cdb996ab6a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "M0HCg1i79OnE6gOgz",
+ "isStandardContent": false,
+ "name": "PHS_1_7X20_5_DC10_2 <1>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..9a284934b4a1faf77ea41e7d4581f7776624a515
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ceba1b5c64923e7b29c5a686d753748b8a99496890ebe850694cba50d306d690
+size 253184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part
new file mode 100644
index 0000000000000000000000000000000000000000..8c06bdd03deaf955f57e8922c85cfe325257d0ed
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "2cfe27202252cfce42ed7ee4",
+ "fullConfiguration": "default",
+ "id": "MwHtRhbbnn9zJhJOY",
+ "isStandardContent": false,
+ "name": "PHS_1_7X20_5_DC10_3 <1>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl
new file mode 100644
index 0000000000000000000000000000000000000000..e6535203c2c91a6b4920769a140c428760629940
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b446022c08e14a7d7e18ddfc5a5e979347abe1f3165d7d161b630f107964b26
+size 253184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part
new file mode 100644
index 0000000000000000000000000000000000000000..f85a0306ce25ade315501a3aecbf79f892f197f4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MwlYHRbOjWaQ7Zawb",
+ "isStandardContent": false,
+ "name": "Plateform <1>",
+ "partId": "J4D",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl
new file mode 100644
index 0000000000000000000000000000000000000000..939d8dc9d7b223eed0817fbd6be09d81b140ac71
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:470e7d22f7218cd5e71866947887e2793d52b6301276e005e313e30a875637f7
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part
new file mode 100644
index 0000000000000000000000000000000000000000..f882825b8a1628740ffc7aa73466318ab9ba6353
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MO1xdetqkz/QRPtZr",
+ "isStandardContent": false,
+ "name": "PP00XXX_Stewart Rod <6>",
+ "partId": "RcCD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl
new file mode 100644
index 0000000000000000000000000000000000000000..06bde5f4a1879cd3f19dd06b0adafec970f25a7e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:411e6736ad0961062f5bea0283762f654bb728fef310d0993f9f7634c8b5449f
+size 28884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part
new file mode 100644
index 0000000000000000000000000000000000000000..c469af37e2d0078acf71e9716f6ed8bed4947419
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "M9NEGUdCOXFxFyRZI",
+ "isStandardContent": false,
+ "name": "PP01062_Stewart Arm <5>",
+ "partId": "JpD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl
new file mode 100644
index 0000000000000000000000000000000000000000..3afe074da185977ec1f12a7e98e36d7674b458c8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ca4060f4c178a84d4ef862270aa95c40646f2fc77913ac38e1bed40fc1d883c
+size 125184
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part
new file mode 100644
index 0000000000000000000000000000000000000000..7e0cd0650bca5e81cee765cdfd78aad42d3620bc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MdkzMSXOA7GnM40pG",
+ "isStandardContent": false,
+ "name": "PP01063_Stewart Plateform <1>",
+ "partId": "J2D",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl
new file mode 100644
index 0000000000000000000000000000000000000000..e27cf3db9d3bb0fe6b49bb668462f79f18822eb3
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0de1f6657863b4f31f0f2492efc158fddc720272f4a4e38af17f2c10642ffbca
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..4d7a2a307c71a17ea6ada95bd2bde3a79222ea36
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MwQmuwlK0NX0T4TgA",
+ "isStandardContent": false,
+ "name": "PP01064_Stewart Main Plate <1>",
+ "partId": "RBCD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..328e8ca592e84394d2921b60a29b34c6546cee41
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:63b53dfef166197bc6c1f4b9d797b9009672fb963b9a5b6e65acc9f16e1b6219
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..270b9c1937a7e503ad992715b06511ee1a53f9ed
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MzgaD5MojzhSvLjc9",
+ "isStandardContent": false,
+ "name": "PP01065_Stewart Side Plate <2>",
+ "partId": "RgBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..bee55789e54cda969a531ae7db6a0340163e4e7a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:999d01af4da296fa318009982fb2354c5d51eb7810737aef5affca8f3d83daca
+size 293584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..dae987e01bf508c8410b443d2ff654b71b0e1566
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "Mj2rCiqdYnLvw/uK1",
+ "isStandardContent": false,
+ "name": "PP01066_Stewart Mid Plate <1>",
+ "partId": "RgCD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..4a18b059711e1c5fc3196c6addabf201a669d65a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:486214efae3c0d3b72460ba09f0088d33a7ad841c10180b54108427278780789
+size 176784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part
new file mode 100644
index 0000000000000000000000000000000000000000..15c8dbc294b98be512d0c8fcb9b4cb9ba8e8894a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "M1iw9QOQmsFr6efSd",
+ "isStandardContent": false,
+ "name": "PP01067_bottom_body <1>",
+ "partId": "RjDD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl
new file mode 100644
index 0000000000000000000000000000000000000000..8ba8324b88df98dcdd465e3652dc1831af745adb
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca83667a9ff45433ca41b5e20edaabd38cb99f97d19a99576dfece8edf92d78c
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part
new file mode 100644
index 0000000000000000000000000000000000000000..93b4d715885817b5503d7a6123e2d621d46d758a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MLhpIAfnvnDl50gJ1",
+ "isStandardContent": false,
+ "name": "PP01068_top_body <1>",
+ "partId": "RjDH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl
new file mode 100644
index 0000000000000000000000000000000000000000..9e230bdd3aeb428a2897edcf7ab94dd8d066344b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:701c4ee80d16aa5257e706fa9cc0b8bbae2dd26138a6fd7860b99c7455ae3af7
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part
new file mode 100644
index 0000000000000000000000000000000000000000..eb09cbb58b7728ff272868beba06450637f77362
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MbnmqHXEBd3bXe5NJ",
+ "isStandardContent": false,
+ "name": "PP01069_head_shell_front <1>",
+ "partId": "JsD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl
new file mode 100644
index 0000000000000000000000000000000000000000..70ee36704a2e9869c5410d3120fbdffd2bab3aa6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19ceb0df3c6655315ea012d4cb2622245d28eb302cbb690c859170ebd4b07425
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part
new file mode 100644
index 0000000000000000000000000000000000000000..7a9bfa9fea6b2a888bf4e8f16cb5335eb85a034c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MJZxED2VVtdkQeJoM",
+ "isStandardContent": false,
+ "name": "PP01070_head_head_back <1>",
+ "partId": "JsH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl
new file mode 100644
index 0000000000000000000000000000000000000000..21b85657fc86cd8d0aed9dfb4bc363735c19ff3b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:27a4689edc4380df51b18ec336a80755b66a9b2cb7b4653f205d96d026a5a612
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part
new file mode 100644
index 0000000000000000000000000000000000000000..4969c1b39a66743411e6c2d1d0a167d7c2d2e120
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MK6xlP0iZUknxGJ1B",
+ "isStandardContent": false,
+ "name": "PP01071_Turning bowl <1>",
+ "partId": "R1DD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl
new file mode 100644
index 0000000000000000000000000000000000000000..864d7f877f3234fac9056ce915d64dd899bb0bee
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b146eebeeaac1be6fe652808db20f524e729011d3b8f82dc1a8cdc34922a2deb
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part
new file mode 100644
index 0000000000000000000000000000000000000000..4c48ab98e7540deb8a41bf445b7a80b5e27f4082
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MmSemALQTmY3L94+u",
+ "isStandardContent": false,
+ "name": "PP01072_Turning End <1>",
+ "partId": "RlFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl
new file mode 100644
index 0000000000000000000000000000000000000000..0e13f4bc5d3382938df9d2396f404e9032e189f5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c5a7353f73938b1023ca3dbbb4ad8a01faf5e144405ed342f535df16b004b418
+size 314484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part
new file mode 100644
index 0000000000000000000000000000000000000000..247efb33851e5e31ef47b1bee7bd0b0f804ac041
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "Mc2vPy/hERQOFTVCl",
+ "isStandardContent": false,
+ "name": "PP01078_glasses <1>",
+ "partId": "JXD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl
new file mode 100644
index 0000000000000000000000000000000000000000..1408f21c089be3d0264c476e257b79ca1541e5f4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a4b8b4ac202d582be3b2c3f4f9728ba582514625c3e27572262bbd7a2704b40
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part
new file mode 100644
index 0000000000000000000000000000000000000000..b66e78d8b3f5e72e5e129f6ee322b9e02f3f4302
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "MZ30fBArLgpdr3sZr",
+ "isStandardContent": false,
+ "name": "PP01079_back_big_eye <1>",
+ "partId": "RJBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl
new file mode 100644
index 0000000000000000000000000000000000000000..7343b044e7938d61761001e9b41866f949d67343
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4081dc49bd47072d7cb11d725cb26a72d49af70a3fc0fc09d8b711bdbb1c98d1
+size 93684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part
new file mode 100644
index 0000000000000000000000000000000000000000..d2386d8e648cf63b3fae83a2755b7a7ba8e8c4c0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "M5wrNragj9mzp/cuf",
+ "isStandardContent": false,
+ "name": "PP01080_back_small_eye <1>",
+ "partId": "RNBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl
new file mode 100644
index 0000000000000000000000000000000000000000..7ce1913c31c92e6fa3fb95dce3978143030f0c71
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:734c337c5da05b7b1966fbc5b1a2daa64f5e8ddb885ecfae94dd0cf041ca2925
+size 50484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part
new file mode 100644
index 0000000000000000000000000000000000000000..0e49bdf2be8c70676127fbf5389c5dda42753860
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "ME3Sp1txkoCjeAr4W",
+ "isStandardContent": false,
+ "name": "PP01102_ArduCam_Carter <1>",
+ "partId": "KFLB",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl
new file mode 100644
index 0000000000000000000000000000000000000000..866f890e8e519bdc0f08e10fe5d9f1a76e580522
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:edc115b09647cafaf2fd023cd940817b54c33ce04496e586fe9031bd7582c5c6
+size 109484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part
new file mode 100644
index 0000000000000000000000000000000000000000..03ff71e00212296f511feda97e8cca73babaa5b0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MdJ+UkyUvvt4o0cNw",
+ "isStandardContent": false,
+ "name": "Rod <6>",
+ "partId": "R5BD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl
new file mode 100644
index 0000000000000000000000000000000000000000..77d6b21aab89df33bd2a5eb442f1d9d7f3e77de0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b1d0060be86f76bb7481fe3f9d6a02bf61386617ade8fb56cd8b5c449708be7
+size 28884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part
new file mode 100644
index 0000000000000000000000000000000000000000..81f2babe27f3286bf15f9b717d7b6e539fe75725
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "0aa833fd983f27ea7fb43256",
+ "fullConfiguration": "default",
+ "id": "M53IEP0FWpyN7VrPP",
+ "isStandardContent": false,
+ "name": "Shape <12>",
+ "partId": "JHD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl
new file mode 100644
index 0000000000000000000000000000000000000000..104739a309cb7e9da3f3022fff94584ef4893cf5
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6d24f09564c2713b74b392536023b4e055c013af91ee21252a8109ccd1327f8a
+size 286284
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part
new file mode 100644
index 0000000000000000000000000000000000000000..11b71e68f3f345aa54435765fca4b18117149b75
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "f05b326e391ef37011cd1007",
+ "fullConfiguration": "default",
+ "id": "MZ6XefFx77VRcACDn",
+ "isStandardContent": false,
+ "name": "Side Plate <1>",
+ "partId": "RUBD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl
new file mode 100644
index 0000000000000000000000000000000000000000..09b2c1b45ddf2c451b1e95e7bdd4f588139a7faa
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52878966da6e718df392da535a8d0b5a03b99b242e4e44d9b2303ddce969f7c9
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part
new file mode 100644
index 0000000000000000000000000000000000000000..6d8029c1ac9decf8459f1016f4daa8fbbded13cc
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "1aa49f5cc5d823754d8b1fec",
+ "fullConfiguration": "default",
+ "id": "M+k761oFhfeIyvr/G",
+ "isStandardContent": false,
+ "name": "small_lens <1>",
+ "partId": "J1D",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl
new file mode 100644
index 0000000000000000000000000000000000000000..02df87e15a2ccfbbbb25da45eac50df2e7e8c0eb
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8d284a1f2f79d7df815bcac24427b7167d0fba81bb125c00c7e451215047f71e
+size 158484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part
new file mode 100644
index 0000000000000000000000000000000000000000..3d88ba9f73298b3d974f455671f78b836df1be85
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "f67f6ec9755019fa8d4cde38",
+ "fullConfiguration": "default",
+ "id": "MeOBTO0SVYe+Ls79P",
+ "isStandardContent": false,
+ "name": "Small Lens D30 <1>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl
new file mode 100644
index 0000000000000000000000000000000000000000..8094f7e32450ded9ed331ccd7bbb17c5e4a806b0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c3f62174bc76b85b39e7835fc9e005fabde685677576c6c1e40753417fa922ec
+size 158484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part
new file mode 100644
index 0000000000000000000000000000000000000000..d08be9b47acf91ee5d22bccd38e8c1fe8284a087
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "0c90fa98e702b6ba866efe06",
+ "fullConfiguration": "default",
+ "id": "MLAaNJd+l2AB7zwWF",
+ "isStandardContent": false,
+ "name": "stewart_link_ball <7>",
+ "partId": "JFD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl
new file mode 100644
index 0000000000000000000000000000000000000000..1a9a1ea300afff8876d32f887b84d0d6784c54b6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d24b6357fe780b9f54a550278fded8886524b732e2ec7765eeed1a8eee0e94bd
+size 194484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part
new file mode 100644
index 0000000000000000000000000000000000000000..f6c45dff65f1e1ee06039c76d2a3bb0b6bf897b7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "0c90fa98e702b6ba866efe06",
+ "fullConfiguration": "default",
+ "id": "Mzs67Fs/V5a1ifM8W",
+ "isStandardContent": false,
+ "name": "stewart_link_ball <10>",
+ "partId": "JGD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl
new file mode 100644
index 0000000000000000000000000000000000000000..24023fd4e3c3ca9000ffbb92ce8b56021836b330
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:25041196fb7b87a0118d1b283e97bcd56a961aafd08527f1408cc23077e77285
+size 194484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part
new file mode 100644
index 0000000000000000000000000000000000000000..e64de22e38d2d2ce466a6de4370365f6e33808d8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "0c90fa98e702b6ba866efe06",
+ "fullConfiguration": "default",
+ "id": "MHKZQKk3gjSPuG0gI",
+ "isStandardContent": false,
+ "name": "stewart_link_rod <6>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl
new file mode 100644
index 0000000000000000000000000000000000000000..cea3397c14840081030e71bdd1935572930344fe
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ecacaefa0d9407ee902fcddac86927a27b09a352c4539347dbe8dfcd827af986
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..08c274a4c5cd80ec13af03f40db911086b0db3bf
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "Mi3j82DWKTUoiYXon",
+ "isStandardContent": false,
+ "name": "STEWART_MAIN_PLATE_3DPrint <1>",
+ "partId": "JFT",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..f90d29504f1883cdb6bc5b71366e1d0068638c87
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9577be3c17eeb3447e5bb7e3a2ffab3f40d951b2b341ca25e06e74c35997780d
+size 314484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part
new file mode 100644
index 0000000000000000000000000000000000000000..b5c08005958d4e04162360d109fa55660e0fa8d8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "7836ed39be931c6ece17e007",
+ "documentMicroversion": "22df16c0b36fccefd62a3ccb",
+ "elementId": "41e19b342cf83e0d866fde9b",
+ "fullConfiguration": "default",
+ "id": "M8OX4JdBklJa3ZAV2",
+ "isStandardContent": false,
+ "name": "STEWART_TRICAP_3DPrint <1>",
+ "partId": "JFX",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl
new file mode 100644
index 0000000000000000000000000000000000000000..73766e4d18e34c5ccc941c369f9ba1a3949c3dce
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44c9acd2445753459253e6a1a8cd8b5036e8d9d98becde750cdeb950a01500d4
+size 314484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part
new file mode 100644
index 0000000000000000000000000000000000000000..74857f2a9cc26c8648436c06107dd3b9198b06d1
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MoUkZchlIKO7O02cZ",
+ "isStandardContent": false,
+ "name": "test_antenna <1>",
+ "partId": "RMCD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl
new file mode 100644
index 0000000000000000000000000000000000000000..a16be17aeed6b22f7554c2ccd95623f805fcfdc4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f2f1f31344e0b21ad6b433953b86713d3834e54c87e23e4f626e77e390bce6da
+size 266484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part
new file mode 100644
index 0000000000000000000000000000000000000000..1ae8057428dff553b6b01d44638629bb882a5eab
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "MrCYnP6eA71aF9l+x",
+ "isStandardContent": false,
+ "name": "test_antenna_body <1>",
+ "partId": "RFCD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl
new file mode 100644
index 0000000000000000000000000000000000000000..648959fd43206ee42bdc504730e2bbb6d3718c57
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0f9dd0adbe7a9243b2a527d4c5cc0d960ae2e15148d3ab8882b8a2389864094d
+size 314484
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part
new file mode 100644
index 0000000000000000000000000000000000000000..09d9a2c6d294b3ae46402001f84fab987429a9bd
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "Ma4FMD0Z/1T41GJcv",
+ "isStandardContent": false,
+ "name": "top_body <1>",
+ "partId": "RcDH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl
new file mode 100644
index 0000000000000000000000000000000000000000..c8aeab9b0186ff0fec4b0fafea041297fc58b326
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5c560740256a73f6dd3adeae424380fbf75acfe7056fdc1858a100cb7806fb1
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part
new file mode 100644
index 0000000000000000000000000000000000000000..a3e694b52b3a8e66e9404230e5cbd5ea4d2a8ac7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "7031fc46a026e2d553617dca",
+ "elementId": "63ac215c84389096e60ba3ce",
+ "fullConfiguration": "default",
+ "id": "M8Ekuan84MeJgvaNm",
+ "isStandardContent": false,
+ "name": "Turning bowl <1>",
+ "partId": "RuDD",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl
new file mode 100644
index 0000000000000000000000000000000000000000..9521ffb779dbc83c142c213851ba8e22bb5fadbd
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8af1cf9e3fb58111f59b95404a4b6e9f5edcc4cd23d0e5a3e74ad82bcce574c8
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part
new file mode 100644
index 0000000000000000000000000000000000000000..ecc0cc1664bd4dcda7fe4bc50f79285a03517c1e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part
@@ -0,0 +1,13 @@
+{
+ "configuration": "default",
+ "documentId": "e305b26a84339af20c593973",
+ "documentMicroversion": "4913859d0d2ce0a13b3adb26",
+ "elementId": "022097d02976f241add4ce3a",
+ "fullConfiguration": "default",
+ "id": "MHLMmdBzC0KnRb5Hr",
+ "isStandardContent": false,
+ "name": "UC-A37_Rev.A.step <1>",
+ "partId": "KF7D",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl
new file mode 100644
index 0000000000000000000000000000000000000000..47120af1c31a770be1c070c44baeffcf9f66e749
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f77f8ca32283c4d1953510273776dd7fdc2d3ae817be8f82750023e68a9cd3df
+size 68784
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part
new file mode 100644
index 0000000000000000000000000000000000000000..a27769e55e1a621c39401c9aa2189a2c94fcb542
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Default",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "9cbc0f0673222989756dc3bd",
+ "documentVersion": "ab0d611cff58eb03c4b43922",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Default",
+ "id": "MICc+zB1tkivCW+Y6",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0122TOPCABINETCASE_95 <1>",
+ "partId": "JFT",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl
new file mode 100644
index 0000000000000000000000000000000000000000..f3c50e4212a61ab269622111df8fa7a69199cb81
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a28441cb86868c45f3910d7e88a583b9b561f7abd061e4c1064abe8b18523513
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part
new file mode 100644
index 0000000000000000000000000000000000000000..b0a107caf01cdca78e1e2f28beed5c28d088b0e4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "562fbad41f5c94b4e8929753",
+ "documentVersion": "b2b50e6fca74f541285598d2",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "id": "McPFfHZbW2/EPDp0S",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0122TOPCABINETCASE_95 <1>",
+ "partId": "JFT",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2ee91f3dc788cbe6172d83f5ade0539245d84eed
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8eb77f6be288d16c44f0869824e53124b0fc05d70acf84f257ae0e91aa81bd04
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part
new file mode 100644
index 0000000000000000000000000000000000000000..3c02cc5621688412646c78aa68a0c8eb360b71f9
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Default",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "9cbc0f0673222989756dc3bd",
+ "documentVersion": "ab0d611cff58eb03c4b43922",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Default",
+ "id": "MX9cSWxeY0rpuKITc",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0123MIDDLECASE_56 <1>",
+ "partId": "JFP",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl
new file mode 100644
index 0000000000000000000000000000000000000000..eaaf8c5b50f7d96251041f901745f6ef8b761058
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a4d233ef0122e27951d21461d8c30e90285708ede5672962e0340fe0185fc0ac
+size 49684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part
new file mode 100644
index 0000000000000000000000000000000000000000..bfdf0e9889107b6794bfaf91639ad28b772f3e21
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "562fbad41f5c94b4e8929753",
+ "documentVersion": "b2b50e6fca74f541285598d2",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "id": "MJpNLRylDLT4+V3p5",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0123MIDDLECASE_56 <1>",
+ "partId": "JFP",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl
new file mode 100644
index 0000000000000000000000000000000000000000..4dce18fb456b8b3be8907532c1ac0f8442447b5d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c459b2adcabec2a270ceec40a983cb9c640f01a9736992f276f8487f0d4ffb8
+size 49684
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part
new file mode 100644
index 0000000000000000000000000000000000000000..e03ef9ac01beddcec14720ed83198b121a23a7d0
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Default",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "9cbc0f0673222989756dc3bd",
+ "documentVersion": "ab0d611cff58eb03c4b43922",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Default",
+ "id": "Mp+b1niKgywMWznYQ",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0124BOTTOMCASE_45 <1>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2d0274d116f81dd6b0ba2f764a2327e6e785ea6f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:651f10e580d0390ab84c1fbd3de339eabad094d5da678d4d62465abce8431178
+size 314584
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part
new file mode 100644
index 0000000000000000000000000000000000000000..dc46c65e95a8bd150c8ef6903a39c6a7be5da89a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part
@@ -0,0 +1,14 @@
+{
+ "configuration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "documentId": "4e2472e743f2deb9dbfd3f29",
+ "documentMicroversion": "562fbad41f5c94b4e8929753",
+ "documentVersion": "b2b50e6fca74f541285598d2",
+ "elementId": "cb09afde2287e85126e219d6",
+ "fullConfiguration": "List_RLLNj5hPLm22K3=Simple_Axe",
+ "id": "MGhQLzn6iUDbdNEjQ",
+ "isStandardContent": false,
+ "name": "WJ-WK00-0124BOTTOMCASE_45 <1>",
+ "partId": "JFH",
+ "suppressed": false,
+ "type": "Part"
+}
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl
new file mode 100644
index 0000000000000000000000000000000000000000..2709632d96b4eeab9d6192666be9a32e45f9e066
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f3b671571125f05c7069b1497e2c1b3f7db9ab579000e7c105b8f0a4b36808fe
+size 104884
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/config.json b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..10016f3b942b411477a38c0b2e81c94015eff08e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/config.json
@@ -0,0 +1,18 @@
+{
+ "url": "https://cad.onshape.com/documents/7836ed39be931c6ece17e007/w/39d557f31a0ba179c9e7bd5b/e/c82436d68c7d00e142abc581",
+ "outputFormat": "urdf",
+ "robot_name": "robot",
+ "output_filename": "robot",
+ "ball_to_euler": true,
+ "ball_to_euler_order": "xyz",
+ "simplify_stls": true,
+ "max_stl_size": 0.3,
+ "joint_properties": {
+ "*": {
+ "max_velocity": 8
+ },
+ "passive*": {
+ "max_velocity": 1e8
+ }
+ }
+}
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf
new file mode 100644
index 0000000000000000000000000000000000000000..f141d8a4cc88feabc83536567620235346edb0b1
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf
@@ -0,0 +1,3281 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak
new file mode 100644
index 0000000000000000000000000000000000000000..65c5c15b36b9bf51cb60e1fce6515c11871da14f
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak
@@ -0,0 +1,3282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf
new file mode 100644
index 0000000000000000000000000000000000000000..4317f7e53fd0e09f5de2d30bf14c083549f22e84
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf
@@ -0,0 +1,2316 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf
new file mode 100644
index 0000000000000000000000000000000000000000..1326b3e7848b40c1ebad6fb7cabb703c42bc5e0d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf
@@ -0,0 +1,2399 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reachy_mini/src/reachy_mini/io/__init__.py b/reachy_mini/src/reachy_mini/io/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0888e2576d28a98185f8b32543808ab48c3965c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/io/__init__.py
@@ -0,0 +1 @@
+"""IO module."""
diff --git a/reachy_mini/src/reachy_mini/io/abstract.py b/reachy_mini/src/reachy_mini/io/abstract.py
new file mode 100644
index 0000000000000000000000000000000000000000..24b16ab743ef5a9f59d57769d4f4b995cfa7f191
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/io/abstract.py
@@ -0,0 +1,70 @@
+"""Base classes for server and client implementations.
+
+These abstract classes define the interface for server and client components
+in the Reachy Mini project. They provide methods for starting and stopping
+the server, handling commands, and managing client connections.
+"""
+
+from abc import ABC, abstractmethod
+from threading import Event
+from uuid import UUID
+
+from reachy_mini.io.protocol import AnyTaskRequest
+
+
+class AbstractServer(ABC):
+ """Base class for server implementations."""
+
+ @abstractmethod
+ def start(self) -> None:
+ """Start the server."""
+ pass
+
+ @abstractmethod
+ def stop(self) -> None:
+ """Stop the server."""
+ pass
+
+ @abstractmethod
+ def command_received_event(self) -> Event:
+ """Wait for a new command and return it."""
+ pass
+
+
+class AbstractClient(ABC):
+ """Base class for client implementations."""
+
+ @abstractmethod
+ def wait_for_connection(self) -> None:
+ """Wait for the client to connect to the server."""
+ pass
+
+ @abstractmethod
+ def is_connected(self) -> bool:
+ """Check if the client is connected to the server."""
+ pass
+
+ @abstractmethod
+ def disconnect(self) -> None:
+ """Disconnect the client from the server."""
+ pass
+
+ @abstractmethod
+ def send_command(self, command: str) -> None:
+ """Send a command to the server."""
+ pass
+
+ @abstractmethod
+ def get_current_joints(self) -> tuple[list[float], list[float]]:
+ """Get the current joint positions."""
+ pass
+
+ @abstractmethod
+ def send_task_request(self, task_req: AnyTaskRequest) -> UUID:
+ """Send a task request to the server and return a unique task identifier."""
+ pass
+
+ @abstractmethod
+ def wait_for_task_completion(self, task_uid: UUID, timeout: float = 5.0) -> None:
+ """Wait for the specified task to complete."""
+ pass
diff --git a/reachy_mini/src/reachy_mini/io/protocol.py b/reachy_mini/src/reachy_mini/io/protocol.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1ecfdd2d36a953c026e6fe3839a796894309328
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/io/protocol.py
@@ -0,0 +1,44 @@
+"""Protocol definitions for Reachy Mini client/server communication."""
+
+from datetime import datetime
+from uuid import UUID
+
+from pydantic import BaseModel
+
+from reachy_mini.utils.interpolation import InterpolationTechnique
+
+
+class GotoTaskRequest(BaseModel):
+ """Class to represent a goto target task."""
+
+ head: list[float] | None # 4x4 flatten pose matrix
+ antennas: list[float] | None # [right_angle, left_angle] (in rads)
+ duration: float
+ method: InterpolationTechnique
+ body_yaw: float | None
+
+
+class PlayMoveTaskRequest(BaseModel):
+ """Class to represent a play move task."""
+
+ move_name: str
+
+
+AnyTaskRequest = GotoTaskRequest | PlayMoveTaskRequest
+
+
+class TaskRequest(BaseModel):
+ """Class to represent any task request."""
+
+ uuid: UUID
+ req: AnyTaskRequest
+ timestamp: datetime
+
+
+class TaskProgress(BaseModel):
+ """Class to represent task progress."""
+
+ uuid: UUID
+ finished: bool = False
+ error: str | None = None
+ timestamp: datetime
diff --git a/reachy_mini/src/reachy_mini/io/zenoh_client.py b/reachy_mini/src/reachy_mini/io/zenoh_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..1668470bf0c8a9645e5234273f4c4526b96af140
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/io/zenoh_client.py
@@ -0,0 +1,258 @@
+"""Zenoh client for Reachy Mini.
+
+This module implements a Zenoh client that allows communication with the Reachy Mini
+robot. It subscribes to joint positions updates and allows sending commands to the robot.
+"""
+
+import json
+import threading
+import time
+from dataclasses import dataclass
+from datetime import datetime
+from typing import Any, Dict, List, Optional
+from uuid import UUID, uuid4
+
+import numpy as np
+import numpy.typing as npt
+import zenoh
+
+from reachy_mini.io.abstract import AbstractClient
+from reachy_mini.io.protocol import AnyTaskRequest, TaskProgress, TaskRequest
+
+
+class ZenohClient(AbstractClient):
+ """Zenoh client for Reachy Mini."""
+
+ def __init__(self, localhost_only: bool = True):
+ """Initialize the Zenoh client."""
+ if localhost_only:
+ c = zenoh.Config.from_json5(
+ json.dumps(
+ {
+ "connect": {
+ "endpoints": {
+ "peer": ["tcp/localhost:7447"],
+ "router": [],
+ },
+ },
+ }
+ )
+ )
+ else:
+ c = zenoh.Config()
+
+ self.joint_position_received = threading.Event()
+ self.head_pose_received = threading.Event()
+ self.status_received = threading.Event()
+
+ self.session = zenoh.open(c)
+ self.cmd_pub = self.session.declare_publisher("reachy_mini/command")
+
+ self.joint_sub = self.session.declare_subscriber(
+ "reachy_mini/joint_positions",
+ self._handle_joint_positions,
+ )
+
+ self.pose_sub = self.session.declare_subscriber(
+ "reachy_mini/head_pose",
+ self._handle_head_pose,
+ )
+
+ self.recording_sub = self.session.declare_subscriber(
+ "reachy_mini/recorded_data",
+ self._handle_recorded_data,
+ )
+
+ self.status_sub = self.session.declare_subscriber(
+ "reachy_mini/daemon_status",
+ self._handle_status,
+ )
+
+ self._last_head_joint_positions = None
+ self._last_antennas_joint_positions = None
+ self._last_head_pose: Optional[npt.NDArray[np.float64]] = None
+ self._recorded_data: Optional[
+ List[Dict[str, float | List[float] | List[List[float]]]]
+ ] = None
+ self._recorded_data_ready = threading.Event()
+ self._is_alive = False
+ self._last_status: Dict[str, Any] = {} # contains a DaemonStatus
+
+ self.tasks: dict[UUID, TaskState] = {}
+ self.task_request_pub = self.session.declare_publisher("reachy_mini/task")
+ self.task_progress_sub = self.session.declare_subscriber(
+ "reachy_mini/task_progress",
+ self._handle_task_progress,
+ )
+
+ def wait_for_connection(self, timeout: float = 5.0) -> None:
+ """Wait for the client to connect to the server.
+
+ Args:
+ timeout (float): Maximum time to wait for the connection in seconds.
+
+ Raises:
+ TimeoutError: If the connection is not established within the timeout period.
+
+ """
+ start = time.time()
+ while not self.joint_position_received.wait(
+ timeout=1.0
+ ) or not self.head_pose_received.wait(timeout=1.0):
+ if time.time() - start > timeout:
+ self.disconnect()
+ raise TimeoutError(
+ "Timeout while waiting for connection with the server."
+ )
+ print("Waiting for connection with the server...")
+
+ self._is_alive = True
+ self._check_alive_evt = threading.Event()
+ threading.Thread(target=self.check_alive, daemon=True).start()
+
+ def check_alive(self) -> None:
+ """Periodically check if the client is still connected to the server."""
+ while True:
+ self._is_alive = self.is_connected()
+ self._check_alive_evt.set()
+ time.sleep(1.0)
+
+ def is_connected(self) -> bool:
+ """Check if the client is connected to the server."""
+ self.joint_position_received.clear()
+ self.head_pose_received.clear()
+ return self.joint_position_received.wait(
+ timeout=1.0
+ ) and self.head_pose_received.wait(timeout=1.0)
+
+ def disconnect(self) -> None:
+ """Disconnect the client from the server."""
+ self.session.close() # type: ignore[no-untyped-call]
+
+ def send_command(self, command: str) -> None:
+ """Send a command to the server."""
+ if not self._is_alive:
+ raise ConnectionError("Lost connection with the server.")
+
+ self.cmd_pub.put(command.encode("utf-8"))
+
+ def _handle_joint_positions(self, sample: zenoh.Sample) -> None:
+ """Handle incoming joint positions."""
+ if sample.payload:
+ positions = json.loads(sample.payload.to_string())
+ self._last_head_joint_positions = positions.get("head_joint_positions")
+ self._last_antennas_joint_positions = positions.get(
+ "antennas_joint_positions"
+ )
+ self.joint_position_received.set()
+
+ def _handle_recorded_data(self, sample: zenoh.Sample) -> None:
+ """Handle incoming recorded data."""
+ print("Received recorded data.")
+ if sample.payload:
+ data = json.loads(sample.payload.to_string())
+ self._recorded_data = data
+ self._recorded_data_ready.set()
+ if self._recorded_data is not None:
+ print(f"Recorded data: {len(self._recorded_data)} frames received.")
+
+ def _handle_status(self, sample: zenoh.Sample) -> None:
+ """Handle incoming status updates."""
+ if sample.payload:
+ status = json.loads(sample.payload.to_string())
+ self._last_status = status
+ self.status_received.set()
+
+ def get_current_joints(self) -> tuple[list[float], list[float]]:
+ """Get the current joint positions."""
+ assert (
+ self._last_head_joint_positions is not None
+ and self._last_antennas_joint_positions is not None
+ ), "No joint positions received yet. Wait for the client to connect."
+ return (
+ self._last_head_joint_positions.copy(),
+ self._last_antennas_joint_positions.copy(),
+ )
+
+ def wait_for_recorded_data(self, timeout: float = 5.0) -> bool:
+ """Block until the daemon publishes the frames (or timeout)."""
+ return self._recorded_data_ready.wait(timeout)
+
+ def get_recorded_data(
+ self, wait: bool = True, timeout: float = 5.0
+ ) -> Optional[List[Dict[str, float | List[float] | List[List[float]]]]]:
+ """Return the cached recording, optionally blocking until it arrives.
+
+ Raises `TimeoutError` if nothing shows up in time.
+ """
+ if wait and not self._recorded_data_ready.wait(timeout):
+ raise TimeoutError("Recording not received in time.")
+ self._recorded_data_ready.clear() # ready for next run
+ if self._recorded_data is not None:
+ return self._recorded_data.copy()
+ return None
+
+ def get_status(self, wait: bool = True, timeout: float = 5.0) -> Dict[str, Any]:
+ """Get the last received status. Returns DaemonStatus as a dict."""
+ if wait and not self.status_received.wait(timeout):
+ raise TimeoutError("Status not received in time.")
+ self.status_received.clear() # ready for next run
+ return self._last_status
+
+ def _handle_head_pose(self, sample: zenoh.Sample) -> None:
+ """Handle incoming head pose."""
+ if sample.payload:
+ pose = json.loads(sample.payload.to_string())
+ self._last_head_pose = np.array(pose.get("head_pose")).reshape(4, 4)
+ self.head_pose_received.set()
+
+ def get_current_head_pose(self) -> npt.NDArray[np.float64]:
+ """Get the current head pose."""
+ assert self._last_head_pose is not None, "No head pose received yet."
+ return self._last_head_pose.copy()
+
+ def send_task_request(self, task_req: AnyTaskRequest) -> UUID:
+ """Send a task request to the server."""
+ if not self._is_alive:
+ raise ConnectionError("Lost connection with the server.")
+
+ task = TaskRequest(uuid=uuid4(), req=task_req, timestamp=datetime.now())
+
+ self.tasks[task.uuid] = TaskState(event=threading.Event(), error=None)
+
+ self.task_request_pub.put(task.model_dump_json())
+
+ return task.uuid
+
+ def wait_for_task_completion(self, task_uid: UUID, timeout: float = 5.0) -> None:
+ """Wait for the specified task to complete."""
+ if task_uid not in self.tasks:
+ raise ValueError("Task not found.")
+
+ self.tasks[task_uid].event.wait(timeout)
+
+ if not self.tasks[task_uid].event.is_set():
+ raise TimeoutError("Task did not complete in time.")
+ if self.tasks[task_uid].error is not None:
+ raise Exception(f"Task failed with error: {self.tasks[task_uid].error}")
+
+ del self.tasks[task_uid]
+
+ def _handle_task_progress(self, sample: zenoh.Sample) -> None:
+ if sample.payload:
+ progress = TaskProgress.model_validate_json(sample.payload.to_string())
+ assert progress.uuid in self.tasks, "Unknown task UUID."
+
+ if progress.error:
+ self.tasks[progress.uuid].error = progress.error
+
+ if progress.finished:
+ self.tasks[progress.uuid].event.set()
+
+
+@dataclass
+class TaskState:
+ """Represents the state of a task."""
+
+ event: threading.Event
+ error: str | None
diff --git a/reachy_mini/src/reachy_mini/io/zenoh_server.py b/reachy_mini/src/reachy_mini/io/zenoh_server.py
new file mode 100644
index 0000000000000000000000000000000000000000..42a96a3e78212070f9ba64986755420c0d6aaf9e
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/io/zenoh_server.py
@@ -0,0 +1,188 @@
+"""Zenoh server for Reachy Mini.
+
+This module implements a Zenoh server that allows communication with the Reachy Mini
+robot. It handles commands for joint positions and torque settings, and publishes joint positions updates.
+
+It uses the Zenoh protocol for efficient data exchange and can be configured to run
+either on localhost only or to accept connections from other hosts.
+"""
+
+import asyncio
+import json
+import threading
+from datetime import datetime
+
+import numpy as np
+import zenoh
+
+from reachy_mini.daemon.backend.abstract import Backend, MotorControlMode
+from reachy_mini.io.abstract import AbstractServer
+from reachy_mini.io.protocol import (
+ GotoTaskRequest,
+ PlayMoveTaskRequest,
+ TaskProgress,
+ TaskRequest,
+)
+
+
+class ZenohServer(AbstractServer):
+ """Zenoh server for Reachy Mini."""
+
+ def __init__(self, backend: Backend, localhost_only: bool = True):
+ """Initialize the Zenoh server."""
+ self.localhost_only = localhost_only
+ self.backend = backend
+
+ self._lock = threading.Lock()
+ self._cmd_event = threading.Event()
+
+ def start(self) -> None:
+ """Start the Zenoh server."""
+ if self.localhost_only:
+ c = zenoh.Config.from_json5(
+ json.dumps(
+ {
+ "listen": {
+ "endpoints": ["tcp/localhost:7447"],
+ },
+ "scouting": {
+ "multicast": {
+ "enabled": False,
+ },
+ "gossip": {
+ "enabled": False,
+ },
+ },
+ "connect": {
+ "endpoints": [
+ "tcp/localhost:7447",
+ ],
+ },
+ }
+ )
+ )
+ else:
+ c = zenoh.Config()
+
+ self.session = zenoh.open(c)
+ self.sub = self.session.declare_subscriber(
+ "reachy_mini/command",
+ self._handle_command,
+ )
+ self.pub = self.session.declare_publisher("reachy_mini/joint_positions")
+ self.pub_record = self.session.declare_publisher("reachy_mini/recorded_data")
+ self.backend.set_joint_positions_publisher(self.pub)
+ self.backend.set_recording_publisher(self.pub_record)
+
+ self.pub_pose = self.session.declare_publisher("reachy_mini/head_pose")
+ self.backend.set_pose_publisher(self.pub_pose)
+
+ self.task_req_sub = self.session.declare_subscriber(
+ "reachy_mini/task",
+ self._handle_task_request,
+ )
+ self.task_progress_pub = self.session.declare_publisher(
+ "reachy_mini/task_progress"
+ )
+
+ self.pub_status = self.session.declare_publisher("reachy_mini/daemon_status")
+
+ def stop(self) -> None:
+ """Stop the Zenoh server."""
+ self.session.close() # type: ignore[no-untyped-call]
+
+ def command_received_event(self) -> threading.Event:
+ """Wait for a new command and return it."""
+ return self._cmd_event
+
+ def _handle_command(self, sample: zenoh.Sample) -> None:
+ data = sample.payload.to_string()
+ command = json.loads(data)
+ with self._lock:
+ if "torque" in command:
+ if (
+ command["ids"] is not None
+ ): # If specific motor IDs are provided, just set torque for those motors
+ self.backend.set_motor_torque_ids(command["ids"], command["torque"])
+ else:
+ if command["torque"]:
+ self.backend.set_motor_control_mode(MotorControlMode.Enabled)
+ else:
+ self.backend.set_motor_control_mode(MotorControlMode.Disabled)
+ if "head_joint_positions" in command:
+ self.backend.set_target_head_joint_positions(
+ np.array(command["head_joint_positions"])
+ )
+ if "head_pose" in command:
+ self.backend.set_target_head_pose(
+ np.array(command["head_pose"]).reshape(4, 4)
+ )
+ if "body_yaw" in command:
+ self.backend.set_target_body_yaw(command["body_yaw"])
+ if "antennas_joint_positions" in command:
+ self.backend.set_target_antenna_joint_positions(
+ np.array(command["antennas_joint_positions"]),
+ )
+ if "gravity_compensation" in command:
+ try:
+ if command["gravity_compensation"]:
+ self.backend.set_motor_control_mode(
+ MotorControlMode.GravityCompensation
+ )
+ else:
+ self.backend.set_motor_control_mode(MotorControlMode.Enabled)
+
+ except ValueError as e:
+ print(e)
+ if "automatic_body_yaw" in command:
+ self.backend.set_automatic_body_yaw(command["automatic_body_yaw"])
+
+ if "set_target_record" in command:
+ self.backend.append_record(command["set_target_record"])
+
+ if "start_recording" in command:
+ self.backend.start_recording()
+ if "stop_recording" in command:
+ self.backend.stop_recording()
+ self._cmd_event.set()
+
+ def _handle_task_request(self, sample: zenoh.Sample) -> None:
+ task_req = TaskRequest.model_validate_json(sample.payload.to_string())
+
+ if isinstance(task_req.req, GotoTaskRequest):
+ req = task_req.req
+
+ def task() -> None:
+ asyncio.run(
+ self.backend.goto_target(
+ head=np.array(req.head).reshape(4, 4) if req.head else None,
+ antennas=np.array(req.antennas) if req.antennas else None,
+ duration=req.duration,
+ method=req.method,
+ body_yaw=req.body_yaw,
+ )
+ )
+ elif isinstance(task_req.req, PlayMoveTaskRequest):
+
+ def task() -> None:
+ print("PLAY MOVE")
+
+ else:
+ assert False, f"Unknown task request type {task_req.req.__class__.__name__}"
+
+ def wrapped_task() -> None:
+ error = None
+ try:
+ task()
+ except Exception as e:
+ error = str(e)
+
+ progress = TaskProgress(
+ uuid=task_req.uuid,
+ finished=True,
+ error=error,
+ timestamp=datetime.now(),
+ )
+ self.task_progress_pub.put(progress.model_dump_json())
+
+ threading.Thread(target=wrapped_task).start()
diff --git a/reachy_mini/src/reachy_mini/kinematics/__init__.py b/reachy_mini/src/reachy_mini/kinematics/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..176bfa7fcc543e97908ec851f7abcbfdffcccd3d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/kinematics/__init__.py
@@ -0,0 +1,68 @@
+"""Try to import kinematics engines, and provide mockup classes if they are not available."""
+
+from typing import Annotated
+
+import numpy as np
+import numpy.typing as npt
+
+try:
+ from reachy_mini.kinematics.nn_kinematics import NNKinematics # noqa: F401
+except ImportError:
+
+ class MockupNNKinematics:
+ """Mockup class for NNKinematics."""
+
+ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
+ """Raise ImportError when trying to instantiate the class."""
+ raise ImportError(
+ "NNKinematics could not be imported. Make sure you run pip install reachy_mini[nn_kinematics]."
+ )
+
+ def ik(self, *args, **kwargs) -> Annotated[npt.NDArray[np.float64], (7,)]: # type: ignore[no-untyped-def]
+ """Mockup method for ik."""
+ raise ImportError(
+ "NNKinematics could not be imported. Make sure you run pip install reachy_mini[nn_kinematics]."
+ )
+
+ def fk(self, *args, **kwargs) -> Annotated[npt.NDArray[np.float64], (4, 4)]: # type: ignore[no-untyped-def]
+ """Mockup method for fk."""
+ raise ImportError(
+ "NNKinematics could not be imported. Make sure you run pip install reachy_mini[nn_kinematics]."
+ )
+
+ NNKinematics = MockupNNKinematics # type: ignore[assignment, misc]
+
+try:
+ from reachy_mini.kinematics.placo_kinematics import PlacoKinematics # noqa: F401
+except ImportError:
+
+ class MockupPlacoKinematics:
+ """Mockup class for PlacoKinematics."""
+
+ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
+ """Raise ImportError when trying to instantiate the class."""
+ raise ImportError(
+ "PlacoKinematics could not be imported. Make sure you run pip install reachy_mini[placo_kinematics]."
+ )
+
+ def ik(self, *args, **kwargs) -> Annotated[npt.NDArray[np.float64], (7,)]: # type: ignore[no-untyped-def]
+ """Mockup method for ik."""
+ raise ImportError(
+ "PlacoKinematics could not be imported. Make sure you run pip install reachy_mini[placo_kinematics]."
+ )
+
+ def fk(self, *args, **kwargs) -> Annotated[npt.NDArray[np.float64], (4, 4)]: # type: ignore[no-untyped-def]
+ """Mockup method for fk."""
+ raise ImportError(
+ "PlacoKinematics could not be imported. Make sure you run pip install reachy_mini[placo_kinematics]."
+ )
+
+ PlacoKinematics = MockupPlacoKinematics # type: ignore[assignment, misc]
+
+
+from reachy_mini.kinematics.analytical_kinematics import ( # noqa: F401
+ AnalyticalKinematics,
+)
+
+AnyKinematics = NNKinematics | PlacoKinematics | AnalyticalKinematics
+__all__ = ["NNKinematics", "PlacoKinematics", "AnalyticalKinematics"]
diff --git a/reachy_mini/src/reachy_mini/kinematics/analytical_kinematics.py b/reachy_mini/src/reachy_mini/kinematics/analytical_kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca7674e8005f0061cc18a3d99c3e05b8b14e7502
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/kinematics/analytical_kinematics.py
@@ -0,0 +1,152 @@
+"""An analytical kinematics engine for Reachy Mini, using Rust bindings.
+
+The inverse kinematics use an analytical method, while the forward kinematics
+use a numerical method (Newton).
+"""
+
+import json
+import logging
+from importlib.resources import files
+from typing import Annotated
+
+import numpy as np
+from numpy.typing import NDArray
+from reachy_mini_rust_kinematics import ReachyMiniRustKinematics
+from scipy.spatial.transform import Rotation as R
+
+import reachy_mini
+
+# Duplicated for now.
+SLEEP_HEAD_POSE = np.array(
+ [
+ [0.911, 0.004, 0.413, -0.021],
+ [-0.004, 1.0, -0.001, 0.001],
+ [-0.413, -0.001, 0.911, -0.044],
+ [0.0, 0.0, 0.0, 1.0],
+ ]
+)
+
+
+class AnalyticalKinematics:
+ """Reachy Mini Analytical Kinematics class, implemented in Rust with python bindings."""
+
+ def __init__(
+ self,
+ automatic_body_yaw: bool = True
+ ) -> None:
+ """Initialize."""
+ assets_root_path: str = str(files(reachy_mini).joinpath("assets/"))
+ data_path = assets_root_path + "/kinematics_data.json"
+ data = json.load(open(data_path, "rb"))
+
+ self.automatic_body_yaw = automatic_body_yaw
+
+ self.head_z_offset = data["head_z_offset"]
+
+ self.kin = ReachyMiniRustKinematics(
+ data["motor_arm_length"], data["rod_length"]
+ )
+
+ self.start_body_yaw = 0.0
+
+ self.motors = data["motors"]
+ for motor in self.motors:
+ self.kin.add_branch(
+ motor["branch_position"],
+ np.linalg.inv(motor["T_motor_world"]), # type: ignore[arg-type]
+ 1 if motor["solution"] else -1,
+ )
+
+ # TODO test with init head pose instead of sleep pose
+ sleep_head_pose = SLEEP_HEAD_POSE.copy()
+ sleep_head_pose[:3, 3][2] += self.head_z_offset
+ self.kin.reset_forward_kinematics(sleep_head_pose) # type: ignore[arg-type]
+
+ self.logger = logging.getLogger(__name__)
+ # self.logger.setLevel(logging.WARNING)
+
+ def ik(
+ self,
+ pose: Annotated[NDArray[np.float64], (4, 4)],
+ body_yaw: float = 0.0,
+ check_collision: bool = False,
+ no_iterations: int = 0,
+ ) -> Annotated[NDArray[np.float64], (7,)]:
+ """Compute the inverse kinematics for a given head pose.
+
+ check_collision and no_iterations are not used by AnalyticalKinematics. We keep them for compatibility with the other kinematics engines
+ """
+ _pose = pose.copy()
+ _pose[:3, 3][2] += self.head_z_offset
+
+ reachy_joints = []
+ if self.automatic_body_yaw:
+ # inverse kinematics solution that modulates the body yaw to
+ # stay within the mechanical limits (max_body_yaw)
+ # additionally it makes sure the the relative yaw between the body and the head
+ # stays within the mechanical limits (max_relative_yaw)
+ reachy_joints = self.kin.inverse_kinematics_safe(_pose, # type: ignore[arg-type]
+ body_yaw = body_yaw,
+ max_relative_yaw = np.deg2rad(65),
+ max_body_yaw = np.deg2rad(160))
+ else:
+ # direct inverse kinematics solution with given body yaw
+ # it does not modify the body yaw
+ stewart_joints = self.kin.inverse_kinematics(_pose, body_yaw) # type: ignore[arg-type]
+ reachy_joints = [body_yaw] + stewart_joints
+
+ return np.array(reachy_joints)
+
+ def fk(
+ self,
+ joint_angles: Annotated[NDArray[np.float64], (7,)],
+ check_collision: bool = False,
+ no_iterations: int = 3,
+ ) -> Annotated[NDArray[np.float64], (4, 4)]:
+ """Compute the forward kinematics for a given set of joint angles.
+
+ check_collision is not used by AnalyticalKinematics.
+ """
+ body_yaw = joint_angles[0]
+
+ _joint_angles = joint_angles[1:].tolist()
+
+ if no_iterations < 1:
+ raise ValueError("no_iterations must be at least 1")
+
+ T_world_platform = None
+ ok = False
+ while not ok:
+ for _ in range(no_iterations):
+ T_world_platform = np.array(
+ self.kin.forward_kinematics(_joint_angles, body_yaw)
+ )
+ assert T_world_platform is not None
+ euler = R.from_matrix(T_world_platform[:3, :3]).as_euler(
+ "xyz", degrees=True
+ )
+ # check that head is upright. Recompute with epsilon adjustments if not
+ if not (euler[0] > 90 or euler[0] < -90 or euler[1] > 90 or euler[1] < -90):
+ ok = True
+ else:
+ self.logger.warning("Head is not upright, recomputing FK")
+ body_yaw += 0.001
+ _joint_angles = list(np.array(_joint_angles) + 0.001)
+ tmp = np.eye(4)
+ tmp[:3, 3][2] += self.head_z_offset
+ self.kin.reset_forward_kinematics(tmp) # type: ignore[arg-type]
+
+ assert T_world_platform is not None
+
+ T_world_platform[:3, 3][2] -= self.head_z_offset
+
+ return T_world_platform
+
+ def set_automatic_body_yaw(self, automatic_body_yaw: bool) -> None:
+ """Set the automatic body yaw.
+
+ Args:
+ automatic_body_yaw (bool): Whether to enable automatic body yaw.
+
+ """
+ self.automatic_body_yaw = automatic_body_yaw
\ No newline at end of file
diff --git a/reachy_mini/src/reachy_mini/kinematics/nn_kinematics.py b/reachy_mini/src/reachy_mini/kinematics/nn_kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bab191285cdb016d4cd0a17540e9390a6266166
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/kinematics/nn_kinematics.py
@@ -0,0 +1,111 @@
+"""Neural Network based FK/IK."""
+
+import time
+from typing import Annotated
+
+import numpy as np
+import numpy.typing as npt
+import onnxruntime
+from scipy.spatial.transform import Rotation as R
+
+
+class NNKinematics:
+ """Neural Network based FK/IK. Fitted from PlacoKinematics data."""
+
+ def __init__(self, models_root_path: str):
+ """Intialize."""
+ self.fk_model_path = f"{models_root_path}/fknetwork.onnx"
+ self.ik_model_path = f"{models_root_path}/iknetwork.onnx"
+ self.fk_infer = OnnxInfer(self.fk_model_path)
+ self.ik_infer = OnnxInfer(self.ik_model_path)
+
+ self.automatic_body_yaw = False # No used, kept for canaompatibility
+
+ def ik(
+ self,
+ pose: Annotated[npt.NDArray[np.float64], (4, 4)],
+ body_yaw: float = 0.0,
+ check_collision: bool = False,
+ no_iterations: int = 0,
+ ) -> Annotated[npt.NDArray[np.float64], (7,)]:
+ """check_collision and no_iterations are not used by NNKinematics.
+
+ We keep them for compatibility with the other kinematics engines
+ """
+ x, y, z = pose[:3, 3][0], pose[:3, 3][1], pose[:3, 3][2]
+ roll, pitch, yaw = R.from_matrix(pose[:3, :3]).as_euler("xyz")
+
+ yaw += body_yaw
+ input = np.array([x, y, z, roll, pitch, yaw])
+
+ joints = self.ik_infer.infer(input)
+ joints[0] += body_yaw
+
+ return joints
+
+ def fk(
+ self,
+ joint_angles: Annotated[npt.NDArray[np.float64], (7,)],
+ check_collision: bool = False,
+ no_iterations: int = 0,
+ ) -> Annotated[npt.NDArray[np.float64], (4, 4)]:
+ """check_collision and no_iterations are not used by NNKinematics.
+
+ We keep them for compatibility with the other kinematics engines
+ """
+ x, y, z, roll, pitch, yaw = self.fk_infer.infer(joint_angles)
+ pose = np.eye(4)
+ pose[:3, 3] = [x, y, z]
+ pose[:3, :3] = R.from_euler("xyz", [roll, pitch, yaw]).as_matrix()
+ return pose
+
+
+ def set_automatic_body_yaw(self, automatic_body_yaw: bool) -> None:
+ """Set the automatic body yaw.
+
+ Args:
+ automatic_body_yaw (bool): Whether to enable automatic body yaw.
+
+ """
+ self.automatic_body_yaw = automatic_body_yaw
+
+class OnnxInfer:
+ """Infer an onnx model."""
+
+ def __init__(self, onnx_model_path: str) -> None:
+ """Initialize."""
+ self.onnx_model_path = onnx_model_path
+ self.ort_session = onnxruntime.InferenceSession(
+ self.onnx_model_path, providers=["CPUExecutionProvider"]
+ )
+
+ def infer(self, input: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
+ """Run inference on the input."""
+ outputs = self.ort_session.run(None, {"input": [input]})
+ res: npt.NDArray[np.float64] = outputs[0][0]
+ return res
+
+
+
+
+if __name__ == "__main__":
+ nn_kin = NNKinematics(
+ "assets/models",
+ )
+
+ times_fk: list[float] = []
+ times_ik: list[float] = []
+ for i in range(1000):
+ fk_input = np.random.random(7).astype(np.float64)
+ # ik_input = np.random.random(6).astype(np.float64)
+
+ fk_s = time.time()
+ fk_output = nn_kin.fk(fk_input)
+ times_fk.append(time.time() - fk_s)
+
+ # ik_s = time.time()
+ # ik_output = nn_kin.ik(ik_input)
+ # times_ik.append(time.time() - ik_s)
+
+ print(f"Average FK inference time: {np.mean(times_fk) * 1e6} µs")
+ # print(f"Average IK inference time: {np.mean(times_ik) * 1e6} µs")
diff --git a/reachy_mini/src/reachy_mini/kinematics/placo_kinematics.py b/reachy_mini/src/reachy_mini/kinematics/placo_kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..e842fbd4f2b3b6db76bfd58e42a1ca0faf39bcf6
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/kinematics/placo_kinematics.py
@@ -0,0 +1,656 @@
+"""Placo Kinematics for Reachy Mini.
+
+This module provides the PlacoKinematics class for performing inverse and forward kinematics based on the Reachy Mini robot URDF using the Placo library.
+"""
+
+import logging
+from typing import Annotated, List, Optional
+
+import numpy as np
+import numpy.typing as npt
+import pinocchio as pin
+import placo
+from scipy.spatial.transform import Rotation as R
+
+
+class PlacoKinematics:
+ """Placo Kinematics class for Reachy Mini.
+
+ This class provides methods for inverse and forward kinematics using the Placo library and a URDF model of the Reachy Mini robot.
+ """
+
+ def __init__(
+ self,
+ urdf_path: str,
+ dt: float = 0.02,
+ automatic_body_yaw: bool = False,
+ check_collision: bool = False,
+ log_level: str = "INFO",
+ ) -> None:
+ """Initialize the PlacoKinematics class.
+
+ Args:
+ urdf_path (str): Path to the URDF file of the Reachy Mini robot.
+ dt (float): Time step for the kinematics solver. Default is 0.02 seconds.
+ automatic_body_yaw (bool): If True, the body yaw will be used to compute the IK and FK. Default is False.
+ check_collision (bool): If True, checks for collisions after solving IK. (default: False)
+ log_level (str): Logging level for the kinematics computations.
+
+ """
+ self.fk_reached_tol = np.deg2rad(
+ 0.1
+ ) # 0.1 degrees tolerance for the FK reached condition
+
+ if not urdf_path.endswith(".urdf"):
+ urdf_path = f"{urdf_path}/{'robot_simple_collision.urdf' if check_collision else 'robot_no_collision.urdf'}"
+
+ self.robot = placo.RobotWrapper(
+ urdf_path, placo.Flags.collision_as_visual + placo.Flags.ignore_collisions
+ )
+
+ flags = (
+ 0
+ if check_collision
+ else placo.Flags.ignore_collisions + placo.Flags.collision_as_visual
+ )
+ self.robot_ik = placo.RobotWrapper(urdf_path, flags)
+
+ self.ik_solver = placo.KinematicsSolver(self.robot_ik)
+ self.ik_solver.mask_fbase(True)
+
+ self.fk_solver = placo.KinematicsSolver(self.robot)
+ self.fk_solver.mask_fbase(True)
+
+ self.automatic_body_yaw = automatic_body_yaw
+ self.check_collision = check_collision
+
+ self._logger = logging.getLogger(__name__)
+ self._logger.setLevel(log_level)
+
+ # we could go to soft limits to avoid over-constraining the IK
+ # but the current implementation works robustly with hard limits
+ # so we keep the hard limits for now
+ constrant_type = "hard" # "hard" or "soft"
+
+ # IK closing tasks
+ ik_closing_tasks = []
+ for i in range(1, 6):
+ ik_closing_task = self.ik_solver.add_relative_position_task(
+ f"closing_{i}_1", f"closing_{i}_2", np.zeros(3)
+ )
+ ik_closing_task.configure(f"closing_{i}", constrant_type, 1.0)
+ ik_closing_tasks.append(ik_closing_task)
+
+ # FK closing tasks
+ fk_closing_tasks = []
+ for i in range(1, 6):
+ fk_closing_task = self.fk_solver.add_relative_position_task(
+ f"closing_{i}_1", f"closing_{i}_2", np.zeros(3)
+ )
+ fk_closing_task.configure(f"closing_{i}", constrant_type, 1.0)
+ fk_closing_tasks.append(fk_closing_task)
+
+ # Add the constraint between the rotated torso and the head
+ # This will allow independent control of the torso and the head yaw
+ # until this constraint is reached
+ yaw_constraint = self.ik_solver.add_yaw_constraint(
+ "dummy_torso_yaw", "head", np.deg2rad(55.0)
+ )
+ yaw_constraint.configure("rel_yaw", "hard")
+
+ # Add the constraint to avoid the head from looking too far behind
+ # Mostly due to some numerical problems 180 is always a bit tricky
+ # Not really constraining because the this 180 pose is almost not
+ # reachable with the real robot anyway
+ yaw_constraint_abs = self.ik_solver.add_yaw_constraint(
+ "body_foot_3dprint", "head", np.deg2rad(179.0)
+ )
+ yaw_constraint_abs.configure("abs_yaw", "hard")
+
+ # Add a cone constraint for the head to not exceed a certain angle
+ # This is to avoid the head from looking too far up or down
+ self.fk_cone = self.ik_solver.add_cone_constraint(
+ "body_foot_3dprint", "head", np.deg2rad(35.0)
+ )
+ self.fk_cone.configure("cone", "hard")
+ self.fk_yaw_constraint = self.fk_solver.add_yaw_constraint(
+ "dummy_torso_yaw", "head", np.deg2rad(55.0)
+ )
+ self.fk_yaw_constraint.configure("rel_yaw", "hard")
+
+ # Add a cone constraint for the head to not exceed a certain angle
+ # This is to avoid the head from looking too far up or down
+ fk_cone = self.fk_solver.add_cone_constraint(
+ "body_foot_3dprint", "head", np.deg2rad(35.0)
+ )
+ fk_cone.configure("cone", "hard")
+
+ # Z offset for the head to make it easier to compute the IK and FK
+ # This is the height of the head from the base of the robot
+ self.head_z_offset = 0.177 # offset for the head height
+
+ # IK head task
+ self.head_starting_pose = np.eye(4)
+ self.head_starting_pose[:3, 3][2] = self.head_z_offset
+ self.head_frame = self.ik_solver.add_frame_task("head", self.head_starting_pose)
+ # equivalance to ~1cm = 1deg weights
+ # set to 5 to be higher than the 1.0 for the body yaw
+ self.head_frame.configure(
+ "head",
+ "soft",
+ 5.0, # in meters # 1m
+ 5.0, # in radians # 1rad
+ )
+
+ self.head_frame.T_world_frame = self.head_starting_pose
+
+ # regularization
+ self.ik_yaw_joint_task = self.ik_solver.add_joints_task()
+ self.ik_yaw_joint_task.set_joints({"yaw_body": 0})
+ if not self.automatic_body_yaw:
+ self.ik_yaw_joint_task.configure("joints", "soft", 5e-5)
+ else:
+ self.ik_yaw_joint_task.configure("joints", "soft", 3.0)
+
+ # joint limit tasks (values form URDF)
+ self.ik_solver.enable_velocity_limits(True)
+ self.ik_solver.enable_joint_limits(True)
+ self.ik_solver.dt = dt
+
+ # FK joint task
+ self.head_joints_task = self.fk_solver.add_joints_task()
+ self.head_joints_task.configure("joints", "soft", 5.0)
+ # joint limit tasks (values form URDF)
+ self.fk_solver.enable_velocity_limits(True)
+ self.fk_solver.enable_joint_limits(True)
+ self.fk_solver.dt = dt
+
+ # Actuated DoFs
+ self.joints_names = [
+ "yaw_body",
+ "stewart_1",
+ "stewart_2",
+ "stewart_3",
+ "stewart_4",
+ "stewart_5",
+ "stewart_6",
+ ]
+
+ # Passive DoFs to eliminate with constraint jacobian
+ self.passive_joints_names = [
+ "passive_1_x",
+ "passive_1_y",
+ "passive_2_x",
+ "passive_2_y",
+ "passive_3_x",
+ "passive_3_y",
+ "passive_4_x",
+ "passive_4_y",
+ "passive_5_x",
+ "passive_5_y",
+ "passive_6_x",
+ "passive_6_y",
+ "passive_7_x",
+ "passive_7_y",
+ "passive_7_z",
+ ]
+
+ # Retrieving indexes in the jacobian
+ self.passives_idx = [
+ self.robot.get_joint_v_offset(dof) for dof in self.passive_joints_names
+ ]
+ self.actives_idx = [
+ self.robot.get_joint_v_offset(dof)
+ for dof in self.robot.joint_names()
+ if dof not in self.passive_joints_names
+ ]
+ self.actuated_idx = [
+ self.robot.get_joint_v_offset(dof)
+ for dof in self.robot.joint_names()
+ if dof in self.joints_names
+ ]
+
+ # actuated dof indexes in active dofs
+ self.actuated_idx_in_active = [
+ i for i, idx in enumerate(self.actives_idx) if idx in self.actuated_idx
+ ]
+
+ # set velocity limits to be artificially high
+ # to enable faster convergence of the IK/FK solver
+ max_vel = 13.0 # rad/s
+ for joint_name in self.joints_names:
+ if joint_name != "yaw_body":
+ self.robot.set_velocity_limit(joint_name, max_vel)
+ self.robot_ik.set_velocity_limit(joint_name, max_vel)
+
+ self.robot.set_joint_limits("yaw_body", -2.8, 2.8)
+ self.robot_ik.set_joint_limits("yaw_body", -2.8, 2.8)
+
+ # initial state
+ self._inital_q = self.robot.state.q.copy()
+ self._inital_qd = np.zeros_like(self.robot.state.qd)
+ self._inital_qdd = np.zeros_like(self.robot.state.qdd)
+
+ # initial FK to set the head pose
+ for _ in range(10):
+ self.ik_solver.solve(True) # False to not update the kinematics
+ self.robot_ik.update_kinematics()
+
+ # last good q to revert to in case of collision
+ self._inital_q = self.robot_ik.state.q.copy()
+ self._last_good_q = self.robot_ik.state.q.copy()
+
+ # update the robot state to the initial state
+ self._update_state_to_initial(self.robot) # revert to the inital state
+ self.robot.update_kinematics()
+
+ if self.check_collision:
+ ik_col = self.ik_solver.add_avoid_self_collisions_constraint()
+ ik_col.self_collisions_margin = 0.001 # 1mm
+ ik_col.self_collisions_trigger = 0.002 # 2mm
+ ik_col.configure("avoid_self_collisions", "hard")
+
+ # setup the collision model
+ self.config_collision_model()
+
+ def _update_state_to_initial(self, robot: placo.RobotWrapper) -> None:
+ """Update the robot state to the initial state.
+
+ It does not call update_kinematics, so the robot state is not updated.
+
+ Args:
+ robot (placo.RobotWrapper): The robot wrapper instance to update.
+
+ """
+ robot.state.q = self._inital_q
+ robot.state.qd = self._inital_qd
+ robot.state.qdd = self._inital_qdd
+
+ def _pose_distance(
+ self, pose1: npt.NDArray[np.float64], pose2: npt.NDArray[np.float64]
+ ) -> tuple[float, float]:
+ """Compute the orientation distance between two poses.
+
+ Args:
+ pose1 (np.ndarray): The first pose (4x4 homogeneous transformation matrix).
+ pose2 (np.ndarray): The second pose (4x4 homogeneous transformation matrix).
+
+ Returns:
+ float: The Euler distance between the two poses.
+
+ """
+ euler1 = R.from_matrix(pose1[:3, :3]).as_euler("xyz")
+ euler2 = R.from_matrix(pose2[:3, :3]).as_euler("xyz")
+ p1 = pose1[:3, 3]
+ p2 = pose2[:3, 3]
+ return float(np.linalg.norm(euler1 - euler2)), float(np.linalg.norm(p1 - p2))
+
+ def _closed_loop_constraints_valid(
+ self, robot: placo.RobotWrapper, tol: float = 1e-2
+ ) -> bool:
+ """Check if all closed-loop constraints are satisfied.
+
+ Args:
+ robot (placo.RobotWrapper): The robot wrapper instance to check.
+ tol (float): The tolerance for checking constraints (default: 1e-2).
+
+ Returns:
+ bool: True if all constraints are satisfied, False otherwise.
+
+ """
+ for i in range(1, 6):
+ pos1 = robot.get_T_world_frame(f"closing_{i}_1")[:3, 3]
+ pos2 = robot.get_T_world_frame(f"closing_{i}_2")[:3, 3]
+ if not np.allclose(pos1, pos2, atol=tol):
+ return False
+ return True
+
+ def _get_joint_values(self, robot: placo.RobotWrapper) -> List[float]:
+ """Get the joint values from the robot state.
+
+ Args:
+ robot (placo.RobotWrapper): The robot wrapper instance to get joint values from.
+
+ Returns:
+ List[float]: A list of joint values.
+
+ """
+ joints = []
+ for joint_name in self.joints_names:
+ joint = robot.get_joint(joint_name)
+ joints.append(joint)
+ return joints
+
+ def ik(
+ self,
+ pose: npt.NDArray[np.float64],
+ body_yaw: float = 0.0,
+ no_iterations: int = 2,
+ ) -> Annotated[npt.NDArray[np.float64], (7,)] | None:
+ """Compute the inverse kinematics for the head for a given pose.
+
+ Args:
+ pose (np.ndarray): A 4x4 homogeneous transformation matrix
+ representing the desired position and orientation of the head.
+ body_yaw (float): Body yaw angle in radians.
+ no_iterations (int): Number of iterations to perform (default: 2). The higher the value, the more accurate the solution.
+
+ Returns:
+ List[float]: A list of joint angles for the head.
+
+ """
+ _pose = pose.copy()
+ # set the head pose
+ _pose[:3, 3][2] += self.head_z_offset # offset the height of the head
+ self.head_frame.T_world_frame = _pose
+ # update the body_yaw task
+ self.ik_yaw_joint_task.set_joints({"yaw_body": body_yaw})
+
+ # check the starting configuration
+ # if the poses are too far start from the initial configuration
+ _dist_o, _dist_p = self._pose_distance(
+ _pose, self.robot_ik.get_T_world_frame("head")
+ )
+ # if distance too small 0.1mm and 0.1 deg and the QP has converged (almost 0 velocity)
+ _dist_by = np.abs(body_yaw - self.robot_ik.get_joint("yaw_body"))
+ if (
+ _dist_p < 0.1e-4
+ and _dist_o < np.deg2rad(0.01)
+ and _dist_by < np.deg2rad(0.01)
+ and np.linalg.norm(self.robot_ik.state.qd) < 1e-4
+ ):
+ # no need to recalculate - return the current joint values
+ return np.array(
+ self._get_joint_values(self.robot_ik)
+ ) # no need to solve IK
+ if _dist_o >= np.pi:
+ # distance too big between the current and the target pose
+ # start the optim from zero position
+ #
+ # TO INVESTIGATE: Another way to do this would be not to start from 0 but
+ # to set the target pose not to the actual target but to some intermediate pose
+ self._update_state_to_initial(self.robot_ik)
+ self.robot_ik.update_kinematics()
+ self._logger.debug("IK: Poses too far, starting from initial configuration")
+
+ done = True
+ # do the inital ik
+ for i in range(no_iterations):
+ try:
+ self.ik_solver.solve(True) # False to not update the kinematics
+ except Exception as e:
+ self._logger.debug(f"IK solver failed: {e}, retrying...")
+ done = False
+ break
+ self.robot_ik.update_kinematics()
+
+ # if no problem in solving the IK check for constraint violation
+ if done and (not self._closed_loop_constraints_valid(self.robot_ik)):
+ self._logger.debug(
+ "IK: Not all equality constraints are satisfied in IK, retrying..."
+ )
+ done = False
+
+ # if there was an issue start from scratch
+ if not done:
+ # set the initial pose
+ self._update_state_to_initial(self.robot_ik)
+ self.robot_ik.update_kinematics()
+
+ no_iterations += 2 # add a few more iterations
+ # do the inital ik with 10 iterations
+ for i in range(no_iterations):
+ try:
+ self.ik_solver.solve(True) # False to not update the kinematics
+ except Exception as e:
+ self._logger.warning(f"IK solver failed: {e}, no solution found!")
+ return None
+ self.robot_ik.update_kinematics()
+
+ # Get the joint angles
+ return np.array(self._get_joint_values(self.robot_ik))
+
+ def fk(
+ self,
+ joints_angles: Annotated[npt.NDArray[np.float64], (7,)],
+ no_iterations: int = 2,
+ ) -> Optional[npt.NDArray[np.float64]]:
+ """Compute the forward kinematics for the head given joint angles.
+
+ Args:
+ joints_angles (List[float]): A list of joint angles for the head.
+ no_iterations (int): The number of iterations to use for the FK solver. (default: 2), the higher the more accurate the result.
+
+ Returns:
+ np.ndarray: A 4x4 homogeneous transformation matrix
+
+ """
+ # check if we're already there
+ _current_joints = self._get_joint_values(self.robot)
+ # if the joint angles are the same (tol 1e-4) and teh QP has converged (max speed is 1e-4rad/s)
+ # no need to compute the FK
+ if (
+ np.linalg.norm(np.array(_current_joints) - np.array(joints_angles))
+ < self.fk_reached_tol
+ and self.robot.state.qd.max() < 1e-4
+ ):
+ # no need to compute FK
+ T_world_head: npt.NDArray[np.float64] = self.robot.get_T_world_frame("head")
+ T_world_head[:3, 3][2] -= (
+ self.head_z_offset
+ ) # offset the height of the head
+ return T_world_head
+
+ # update the main task
+ self.head_joints_task.set_joints(
+ {
+ "yaw_body": joints_angles[0],
+ "stewart_1": joints_angles[1],
+ "stewart_2": joints_angles[2],
+ "stewart_3": joints_angles[3],
+ "stewart_4": joints_angles[4],
+ "stewart_5": joints_angles[5],
+ "stewart_6": joints_angles[6],
+ }
+ )
+
+ done = True
+ # do the inital ik with 2 iterations
+ for i in range(no_iterations):
+ try:
+ self.fk_solver.solve(True) # False to not update the kinematics
+ except Exception as e:
+ self._logger.debug(f"FK solver failed: {e}, retrying...")
+ done = False
+ break
+ self.robot.update_kinematics()
+
+ if done and (not self._closed_loop_constraints_valid(self.robot)):
+ self._logger.debug(
+ "FK: Not all equality constraints are satisfied in FK, retrying..."
+ )
+ done = False
+
+ if not done:
+ self._update_state_to_initial(self.robot) # revert to the previous state
+ self.robot.update_kinematics()
+
+ no_iterations += 2 # add a few more iterations
+ # do the inital ik with 10 iterations
+ for i in range(no_iterations):
+ try:
+ self.fk_solver.solve(True) # False to not update the kinematics
+ except Exception as e:
+ self._logger.warning(f"FK solver failed: {e}, no solution found!")
+ return None
+ self.robot.update_kinematics()
+
+ # Get the head frame transformation
+ T_world_head = self.robot.get_T_world_frame("head")
+ T_world_head[:3, 3][2] -= self.head_z_offset # offset the height of the head
+
+ return T_world_head
+
+ def config_collision_model(self) -> None:
+ """Configure the collision model for the robot.
+
+ Add collision pairs between the torso and the head colliders.
+ """
+ geom_model = self.robot_ik.collision_model
+
+ id_torso_colliders = list(range(len(geom_model.geometryObjects) - 1))
+ id_head_collider = len(geom_model.geometryObjects) - 1
+
+ for i in id_torso_colliders:
+ geom_model.addCollisionPair(
+ pin.CollisionPair(id_head_collider, i)
+ ) # torso with head colliders
+
+ def compute_collision(self, margin: float = 0.005) -> bool:
+ """Compute the collision between the robot and the environment.
+
+ Args:
+ margin (float): The margin to consider for collision detection (default: 5mm).
+
+ Returns:
+ True if there is a collision, False otherwise.
+
+ """
+ collision_data = self.robot_ik.collision_model.createData()
+ data = self.robot_ik.model.createData()
+
+ # pin.computeCollisions(
+ pin.computeDistances(
+ self.robot_ik.model,
+ data,
+ self.robot_ik.collision_model,
+ collision_data,
+ self.robot_ik.state.q,
+ )
+
+ # Iterate over all collision pairs
+ for distance_result in collision_data.distanceResults:
+ if distance_result.min_distance <= margin:
+ return True # Something is too close or colliding!
+
+ return False # Safe
+
+ def compute_jacobian(
+ self, q: Optional[npt.NDArray[np.float64]] = None
+ ) -> npt.NDArray[np.float64]:
+ """Compute the Jacobian of the head frame with respect to the actuated DoFs.
+
+ The jacobian in local world aligned.
+
+ Args:
+ q (np.ndarray, optional): Joint angles of the robot. If None, uses the current state of the robot. (default: None)
+
+ Returns:
+ np.ndarray: The Jacobian matrix.
+
+ """
+ # If q is provided, use it to compute the forward kinematics
+ if q is not None:
+ self.fk(q, no_iterations=20)
+
+ # Computing the platform Jacobian
+ # dx = Jp.dq
+ Jp: npt.NDArray[np.float64] = self.robot.frame_jacobian(
+ "head", "local_world_aligned"
+ )
+
+ # Computing the constraints Jacobian
+ # 0 = Jc.dq
+ constraints = []
+ for i in range(1, 6):
+ Jc = self.robot.relative_position_jacobian(
+ f"closing_{i}_1", f"closing_{i}_2"
+ )
+ constraints.append(Jc)
+ Jc = np.vstack(constraints)
+
+ # Splitting jacobians as
+ # Jp_a.dq_a + Jp_p.dq_p = dx
+ Jp_a = Jp[:, self.actives_idx]
+ Jp_p = Jp[:, self.passives_idx]
+ # Jc_a.dq_a + Jc_p.dq_p = 0
+ Jc_a = Jc[:, self.actives_idx]
+ Jc_p = Jc[:, self.passives_idx]
+
+ # Computing effector jacobian under constraints
+ # Because constraint equation
+ # Jc_a.dq_a + Jc_p.dq_p = 0
+ # can be written as:
+ # dq_p = - (Jc_p)^(⁻1) @ Jc_a @ dq_a
+ # Then we can substitute dq_p in the first equation and get
+ # This new jacobian
+ J: npt.NDArray[np.float64] = Jp_a - Jp_p @ np.linalg.inv(Jc_p) @ Jc_a
+
+ return J[:, self.actuated_idx_in_active]
+
+ def compute_gravity_torque(
+ self, q: Optional[npt.NDArray[np.float64]] = None
+ ) -> npt.NDArray[np.float64]:
+ """Compute the gravity torque vector for the actuated joints of the robot.
+
+ This method uses the static gravity compensation torques from the robot's dictionary.
+
+ Args:
+ q (np.ndarray, optional): Joint angles of the robot. If None, uses the current state of the robot. (default: None)
+
+ Returns:
+ np.ndarray: The gravity torque vector.
+
+ """
+ # If q is provided, use it to compute the forward kinematics
+ if q is not None:
+ self.fk(q)
+
+ # Get the static gravity compensation torques for all joints
+ # except the mobile base 6dofs
+ grav_torque_all_joints = np.array(
+ list(
+ self.robot.static_gravity_compensation_torques_dict(
+ "body_foot_3dprint"
+ ).values()
+ )
+ )
+
+ # See the paper for more info (equations 4-9):
+ # https://hal.science/hal-03379538/file/BriotKhalil_SpringerEncyclRob_bookchapterPKMDyn.pdf#page=4
+ #
+ # Basically to compute the actuated torques necessary to compensate the gravity, we need to compute the
+ # the equivalent wrench in the head frame that would be created if all the joints were actuated.
+ # wrench_eq = np.linalg.pinv(J_all_joints.T) @ torque_all_joints
+ # And then we can compute the actuated torques as:
+ # torque_actuated = J_actuated.T @ wrench_eq
+ J_all_joints: npt.NDArray[np.float64] = self.robot.frame_jacobian(
+ "head", "local_world_aligned"
+ )[:, 6:] # all joints except the mobile base 6dofs
+ J_actuated = self.compute_jacobian()
+ # using a single matrix G to compute the actuated torques
+ G = J_actuated.T @ np.linalg.pinv(J_all_joints.T)
+
+ # torques of actuated joints
+ grav_torque_actuated: npt.NDArray[np.float64] = G @ grav_torque_all_joints
+
+ # Compute the gravity torque
+ return grav_torque_actuated
+
+ def set_automatic_body_yaw(self, automatic_body_yaw: bool) -> None:
+ """Set the automatic body yaw.
+
+ Args:
+ automatic_body_yaw (bool): Whether to enable automatic body yaw.
+
+ """
+ self.automatic_body_yaw = automatic_body_yaw
+
+ if not self.automatic_body_yaw:
+ self.ik_yaw_joint_task.configure("joints", "soft", 3.0)
+ else:
+ self.ik_yaw_joint_task.configure("joints", "soft", 5e-5)
+
+ def get_joint(self, joint_name: str) -> float:
+ """Get the joint object by its name."""
+ return float(self.robot.get_joint(joint_name))
diff --git a/reachy_mini/src/reachy_mini/media/__init__.py b/reachy_mini/src/reachy_mini/media/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..71432d90862359f05e8c978c2069a4f07eedc8b7
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/__init__.py
@@ -0,0 +1 @@
+"""Media module."""
diff --git a/reachy_mini/src/reachy_mini/media/audio_base.py b/reachy_mini/src/reachy_mini/media/audio_base.py
new file mode 100644
index 0000000000000000000000000000000000000000..4bea6c138e0c449c2eb3bed03b4e51b2393d428d
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/audio_base.py
@@ -0,0 +1,102 @@
+"""Base classes for audio implementations.
+
+The audio implementations support various backends and provide a unified
+interface for audio input/output.
+"""
+
+import logging
+from abc import ABC, abstractmethod
+from typing import Optional
+
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.audio_control_utils import ReSpeaker, init_respeaker_usb
+
+
+class AudioBase(ABC):
+ """Abstract class for opening and managing audio devices."""
+
+ SAMPLE_RATE = 16000 # respeaker samplerate
+
+ def __init__(self, log_level: str = "INFO") -> None:
+ """Initialize the audio device."""
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+ self._respeaker: Optional[ReSpeaker] = init_respeaker_usb()
+
+ def __del__(self) -> None:
+ """Destructor to ensure resources are released."""
+ if self._respeaker:
+ self._respeaker.close()
+
+ @abstractmethod
+ def start_recording(self) -> None:
+ """Start recording audio."""
+ pass
+
+ @abstractmethod
+ def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
+ """Read audio data from the device. Returns the data or None if error."""
+ pass
+
+ def get_input_audio_samplerate(self) -> int:
+ """Get the input samplerate of the audio device."""
+ return self.SAMPLE_RATE
+
+ def get_output_audio_samplerate(self) -> int:
+ """Get the outputsamplerate of the audio device."""
+ return self.SAMPLE_RATE
+
+ @abstractmethod
+ def stop_recording(self) -> None:
+ """Close the audio device and release resources."""
+ pass
+
+ @abstractmethod
+ def start_playing(self) -> None:
+ """Start playing audio."""
+ pass
+
+ @abstractmethod
+ def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
+ """Push audio data to the output device."""
+ pass
+
+ @abstractmethod
+ def stop_playing(self) -> None:
+ """Stop playing audio and release resources."""
+ pass
+
+ @abstractmethod
+ def play_sound(self, sound_file: str) -> None:
+ """Play a sound file.
+
+ Args:
+ sound_file (str): Path to the sound file to play.
+
+ """
+ pass
+
+ def get_DoA(self) -> tuple[float, bool] | None:
+ """Get the Direction of Arrival (DoA) value from the ReSpeaker device.
+
+ The spatial angle is given in radians:
+ 0 radians is left, π/2 radians is front/back, π radians is right.
+
+ Note: The microphone array requires firmware version 2.1.0 or higher to support this feature.
+ The firmware is located in src/reachy_mini/assets/firmware/*.bin.
+ Refer to https://wiki.seeedstudio.com/respeaker_xvf3800_introduction/#update-firmware for the upgrade process.
+
+ Returns:
+ tuple: A tuple containing the DoA value as a float (radians) and the speech detection as a bool, or None if the device is not found.
+
+ """
+ if not self._respeaker:
+ self.logger.warning("ReSpeaker device not found.")
+ return None
+
+ result = self._respeaker.read("DOA_VALUE_RADIANS")
+ if result is None:
+ return None
+ return float(result[0]), bool(result[1])
diff --git a/reachy_mini/src/reachy_mini/media/audio_control_utils.py b/reachy_mini/src/reachy_mini/media/audio_control_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..97aed17cffd75566779dca393f2d5e819ac6c02b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/audio_control_utils.py
@@ -0,0 +1,428 @@
+"""Allows tuning of the XMOS XVF3800 chip.
+
+Example usage:
+
+ # Read a parameter
+ python reachy_host.py AUDIO_MGR_OP_L
+ # Output:
+ # ReadCMD: cmdid: 143, resid: 35, response: array('B', [0, 8, 0])
+ # AUDIO_MGR_OP_L: [0, 8, 0]
+
+ # Write a parameter
+ python reachy_host.py AUDIO_MGR_OP_L --values 3 0
+ # Output:
+ # Writing to AUDIO_MGR_OP_L with values: [3, 0]
+ # WriteCMD: cmdid: 15, resid: 35, payload: [3, 0]
+ # Write operation completed successfully
+
+More details about the parameters is available at:
+https://www.xmos.com/documentation/XM-014888-PC/html/modules/fwk_xvf/doc/user_guide/AA_control_command_appendix.html
+"""
+
+import argparse
+import logging
+import struct
+import sys
+import time
+from typing import Any, Optional
+
+import usb.core
+import usb.util
+from libusb_package import get_libusb1_backend
+
+CONTROL_SUCCESS = 0
+SERVICER_COMMAND_RETRY = 64
+
+# name, resid, cmdid, length, type
+PARAMETERS = {
+ # APPLICATION_SERVICER_RESID commands
+ "VERSION": (48, 0, 3, "ro", "uint8"),
+ "BLD_MSG": (48, 1, 50, "ro", "char"),
+ "BLD_HOST": (48, 2, 30, "ro", "char"),
+ "BLD_REPO_HASH": (48, 3, 40, "ro", "char"),
+ "BLD_MODIFIED": (48, 4, 6, "ro", "char"),
+ "BOOT_STATUS": (48, 5, 3, "ro", "char"),
+ "TEST_CORE_BURN": (48, 6, 1, "rw", "uint8"),
+ "REBOOT": (48, 7, 1, "wo", "uint8"),
+ "USB_BIT_DEPTH": (48, 8, 2, "rw", "uint8"),
+ "SAVE_CONFIGURATION": (48, 9, 1, "wo", "uint8"),
+ "CLEAR_CONFIGURATION": (48, 10, 1, "wo", "uint8"),
+ # AEC_RESID commands
+ "SHF_BYPASS": (33, 70, 1, "rw", "uint8"),
+ "AEC_NUM_MICS": (33, 71, 1, "ro", "int32"),
+ "AEC_NUM_FARENDS": (33, 72, 1, "ro", "int32"),
+ "AEC_MIC_ARRAY_TYPE": (33, 73, 1, "ro", "int32"),
+ "AEC_MIC_ARRAY_GEO": (33, 74, 12, "ro", "float"),
+ "AEC_AZIMUTH_VALUES": (33, 75, 4, "ro", "radians"),
+ "TEST_AEC_DISABLE_CONTROL": (33, 76, 1, "wo", "uint32"),
+ "AEC_CURRENT_IDLE_TIME": (33, 77, 1, "ro", "uint32"),
+ "AEC_MIN_IDLE_TIME": (33, 78, 1, "ro", "uint32"),
+ "AEC_RESET_MIN_IDLE_TIME": (33, 79, 1, "wo", "uint32"),
+ "AEC_SPENERGY_VALUES": (33, 80, 4, "ro", "float"),
+ "AEC_FIXEDBEAMSAZIMUTH_VALUES": (33, 81, 2, "rw", "radians"),
+ "AEC_FIXEDBEAMSELEVATION_VALUES": (33, 82, 2, "rw", "radians"),
+ "AEC_FIXEDBEAMSGATING": (33, 83, 1, "rw", "uint8"),
+ "SPECIAL_CMD_AEC_FAR_MIC_INDEX": (33, 90, 2, "wo", "int32"),
+ "SPECIAL_CMD_AEC_FILTER_COEFF_START_OFFSET": (33, 91, 1, "rw", "int32"),
+ "SPECIAL_CMD_AEC_FILTER_COEFFS": (33, 92, 15, "rw", "float"),
+ "SPECIAL_CMD_AEC_FILTER_LENGTH": (33, 93, 1, "ro", "int32"),
+ "AEC_FILTER_CMD_ABORT": (33, 94, 1, "wo", "int32"),
+ "AEC_AECPATHCHANGE": (33, 0, 1, "ro", "int32"),
+ "AEC_HPFONOFF": (33, 1, 1, "rw", "int32"),
+ "AEC_AECSILENCELEVEL": (33, 2, 2, "rw", "float"),
+ "AEC_AECCONVERGED": (33, 3, 1, "ro", "int32"),
+ "AEC_AECEMPHASISONOFF": (33, 4, 1, "rw", "int32"),
+ "AEC_FAR_EXTGAIN": (33, 5, 1, "rw", "float"),
+ "AEC_PCD_COUPLINGI": (33, 6, 1, "rw", "float"),
+ "AEC_PCD_MINTHR": (33, 7, 1, "rw", "float"),
+ "AEC_PCD_MAXTHR": (33, 8, 1, "rw", "float"),
+ "AEC_RT60": (33, 9, 1, "ro", "float"),
+ "AEC_ASROUTONOFF": (33, 35, 1, "rw", "int32"),
+ "AEC_ASROUTGAIN": (33, 36, 1, "rw", "float"),
+ "AEC_FIXEDBEAMSONOFF": (33, 37, 1, "rw", "int32"),
+ "AEC_FIXEDBEAMNOISETHR": (33, 38, 2, "rw", "float"),
+ # AUDIO_MGR_RESID commands
+ "AUDIO_MGR_MIC_GAIN": (35, 0, 1, "rw", "float"),
+ "AUDIO_MGR_REF_GAIN": (35, 1, 1, "rw", "float"),
+ "AUDIO_MGR_CURRENT_IDLE_TIME": (35, 2, 1, "ro", "int32"),
+ "AUDIO_MGR_MIN_IDLE_TIME": (35, 3, 1, "ro", "int32"),
+ "AUDIO_MGR_RESET_MIN_IDLE_TIME": (35, 4, 1, "wo", "int32"),
+ "MAX_CONTROL_TIME": (35, 5, 1, "ro", "int32"),
+ "RESET_MAX_CONTROL_TIME": (35, 6, 1, "wo", "int32"),
+ "I2S_CURRENT_IDLE_TIME": (35, 7, 1, "ro", "int32"),
+ "I2S_MIN_IDLE_TIME": (35, 8, 1, "ro", "int32"),
+ "I2S_RESET_MIN_IDLE_TIME": (35, 9, 1, "wo", "int32"),
+ "I2S_INPUT_PACKED": (35, 10, 1, "rw", "uint8"),
+ "AUDIO_MGR_SELECTED_AZIMUTHS": (35, 11, 2, "ro", "radians"),
+ "AUDIO_MGR_SELECTED_CHANNELS": (35, 12, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_PACKED": (35, 13, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_UPSAMPLE": (35, 14, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_L": (35, 15, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_L_PK0": (35, 16, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_L_PK1": (35, 17, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_L_PK2": (35, 18, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_R": (35, 19, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_R_PK0": (35, 20, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_R_PK1": (35, 21, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_R_PK2": (35, 22, 2, "rw", "uint8"),
+ "AUDIO_MGR_OP_ALL": (35, 23, 12, "rw", "uint8"),
+ "I2S_INACTIVE": (35, 24, 1, "ro", "uint8"),
+ "AUDIO_MGR_FAR_END_DSP_ENABLE": (35, 25, 1, "rw", "uint8"),
+ "AUDIO_MGR_SYS_DELAY": (35, 26, 1, "rw", "int32"),
+ "I2S_DAC_DSP_ENABLE": (35, 27, 1, "rw", "uint8"),
+ # GPO_SERVICER_RESID commands
+ "GPO_READ_VALUES": (20, 0, 5, "ro", "uint8"),
+ "GPO_WRITE_VALUE": (20, 1, 2, "wo", "uint8"),
+ "GPO_PORT_PIN_INDEX": (20, 2, 2, "rw", "uint32"),
+ "GPO_PIN_VAL": (20, 3, 3, "wo", "uint8"),
+ "GPO_PIN_ACTIVE_LEVEL": (20, 4, 1, "rw", "uint32"),
+ "GPO_PIN_PWM_DUTY": (20, 5, 1, "rw", "uint8"),
+ "GPO_PIN_FLASH_MASK": (20, 6, 1, "rw", "uint32"),
+ "LED_EFFECT": (20, 12, 1, "rw", "uint8"),
+ "LED_BRIGHTNESS": (20, 13, 1, "rw", "uint8"),
+ "LED_GAMMIFY": (20, 14, 1, "rw", "uint8"),
+ "LED_SPEED": (20, 15, 1, "rw", "uint8"),
+ "LED_COLOR": (20, 16, 1, "rw", "uint32"),
+ "LED_DOA_COLOR": (20, 17, 2, "rw", "uint32"),
+ "DOA_VALUE": (20, 18, 2, "ro", "uint32"),
+ "DOA_VALUE_RADIANS": (20, 19, 2, "ro", "radians"),
+ # PP_RESID commands
+ "PP_CURRENT_IDLE_TIME": (17, 70, 1, "ro", "uint32"),
+ "PP_MIN_IDLE_TIME": (17, 71, 1, "ro", "uint32"),
+ "PP_RESET_MIN_IDLE_TIME": (17, 72, 1, "wo", "uint32"),
+ "SPECIAL_CMD_PP_NLMODEL_NROW_NCOL": (17, 90, 2, "ro", "int32"),
+ "SPECIAL_CMD_NLMODEL_START": (17, 91, 1, "wo", "int32"),
+ "SPECIAL_CMD_NLMODEL_COEFF_START_OFFSET": (17, 92, 1, "rw", "int32"),
+ "SPECIAL_CMD_PP_NLMODEL": (17, 93, 15, "rw", "float"),
+ "PP_NL_MODEL_CMD_ABORT": (17, 94, 1, "wo", "int32"),
+ "SPECIAL_CMD_PP_NLMODEL_BAND": (17, 95, 1, "rw", "uint8"),
+ "SPECIAL_CMD_PP_EQUALIZATION_NUM_BANDS": (17, 96, 1, "ro", "int32"),
+ "SPECIAL_CMD_EQUALIZATION_START": (17, 97, 1, "wo", "int32"),
+ "SPECIAL_CMD_EQUALIZATION_COEFF_START_OFFSET": (17, 98, 1, "rw", "int32"),
+ "SPECIAL_CMD_PP_EQUALIZATION": (17, 99, 15, "rw", "float"),
+ "PP_EQUALIZATION_CMD_ABORT": (17, 100, 1, "wo", "int32"),
+ "PP_AGCONOFF": (17, 10, 1, "rw", "int32"),
+ "PP_AGCMAXGAIN": (17, 11, 1, "rw", "float"),
+ "PP_AGCDESIREDLEVEL": (17, 12, 1, "rw", "float"),
+ "PP_AGCGAIN": (17, 13, 1, "rw", "float"),
+ "PP_AGCTIME": (17, 14, 1, "rw", "float"),
+ "PP_AGCFASTTIME": (17, 15, 1, "rw", "float"),
+ "PP_AGCALPHAFASTGAIN": (17, 16, 1, "rw", "float"),
+ "PP_AGCALPHASLOW": (17, 17, 1, "rw", "float"),
+ "PP_AGCALPHAFAST": (17, 18, 1, "rw", "float"),
+ "PP_LIMITONOFF": (17, 19, 1, "rw", "int32"),
+ "PP_LIMITPLIMIT": (17, 20, 1, "rw", "float"),
+ "PP_MIN_NS": (17, 21, 1, "rw", "float"),
+ "PP_MIN_NN": (17, 22, 1, "rw", "float"),
+ "PP_ECHOONOFF": (17, 23, 1, "rw", "int32"),
+ "PP_GAMMA_E": (17, 24, 1, "rw", "float"),
+ "PP_GAMMA_ETAIL": (17, 25, 1, "rw", "float"),
+ "PP_GAMMA_ENL": (17, 26, 1, "rw", "float"),
+ "PP_NLATTENONOFF": (17, 27, 1, "rw", "int32"),
+ "PP_NLAEC_MODE": (17, 28, 1, "rw", "int32"),
+ "PP_MGSCALE": (17, 29, 3, "rw", "float"),
+ "PP_FMIN_SPEINDEX": (17, 30, 1, "rw", "float"),
+ "PP_DTSENSITIVE": (17, 31, 1, "rw", "int32"),
+ "PP_ATTNS_MODE": (17, 32, 1, "rw", "int32"),
+ "PP_ATTNS_NOMINAL": (17, 33, 1, "rw", "float"),
+ "PP_ATTNS_SLOPE": (17, 34, 1, "rw", "float"),
+}
+
+
+class ReSpeaker:
+ """Class to interface with the ReSpeaker XVF3800 USB device."""
+
+ TIMEOUT = 100000
+
+ def __init__(self, dev: usb.core.Device) -> None:
+ """Initialize the ReSpeaker interface with the given USB device."""
+ self.dev = dev
+
+ def write(self, name: str, data_list: Any) -> None:
+ """Write data to a specified parameter on the ReSpeaker device."""
+ try:
+ data = PARAMETERS[name]
+ except KeyError:
+ return
+
+ if data[3] == "ro":
+ raise ValueError("{} is read-only".format(name))
+
+ if len(data_list) != data[2]:
+ raise ValueError("{} value count is not {}".format(name, data[2]))
+
+ windex = data[0] # resid
+ wvalue = data[1] # cmdid
+ data_cnt = data[2] # cnt
+ data_type = data[4] # data type
+ payload = [] # type: ignore[var-annotated]
+
+ if data_type == "float" or data_type == "radians":
+ for i in range(data_cnt):
+ payload += struct.pack(b"f", float(data_list[i]))
+ elif data_type == "char":
+ # For char arrays, convert string to bytes
+ payload = (
+ bytearray(data_list, "utf-8") # type: ignore[assignment]
+ if isinstance(data_list, str)
+ else bytearray(data_list)
+ )
+ elif data_type == "uint8":
+ for i in range(data_cnt):
+ payload += data_list[i].to_bytes(1, byteorder="little")
+ elif data_type == "uint32" or data_type == "int32":
+ for i in range(data_cnt):
+ payload += struct.pack(
+ b"I" if data_type == "uint32" else b"i", data_list[i]
+ )
+ else:
+ # Default to int32 for other types
+ for i in range(data_cnt):
+ payload += struct.pack(b"i", data_list[i])
+
+ logging.debug(
+ "WriteCMD: cmdid: {}, resid: {}, payload: {}".format(
+ wvalue, windex, payload
+ )
+ )
+
+ self.dev.ctrl_transfer(
+ usb.util.CTRL_OUT
+ | usb.util.CTRL_TYPE_VENDOR
+ | usb.util.CTRL_RECIPIENT_DEVICE,
+ 0,
+ wvalue,
+ windex,
+ payload,
+ self.TIMEOUT,
+ )
+
+ def read(self, name: str) -> Any:
+ """Read data from a specified parameter on the ReSpeaker device."""
+ try:
+ data = PARAMETERS[name]
+ except KeyError:
+ return
+
+ read_attempts = 1
+ windex = data[0] # resid
+ wvalue = 0x80 | data[1] # cmdid
+ data_cnt = data[2] # cnt
+ data_type = data[4] # data type
+ if data_type == "uint8" or data_type == "char":
+ length = data_cnt + 1 # 1 byte for status
+ elif (
+ data_type == "float"
+ or data_type == "radians"
+ or data_type == "uint32"
+ or data_type == "int32"
+ ):
+ length = data_cnt * 4 + 1 # 1 byte for status
+
+ response = self.dev.ctrl_transfer(
+ usb.util.CTRL_IN
+ | usb.util.CTRL_TYPE_VENDOR
+ | usb.util.CTRL_RECIPIENT_DEVICE,
+ 0,
+ wvalue,
+ windex,
+ length,
+ self.TIMEOUT,
+ )
+ while True:
+ if read_attempts > 100:
+ raise ValueError("Read attempt exceeds 100 times")
+ if response[0] == CONTROL_SUCCESS:
+ break
+ elif response[0] == SERVICER_COMMAND_RETRY:
+ read_attempts += 1
+ response = self.dev.ctrl_transfer(
+ usb.util.CTRL_IN
+ | usb.util.CTRL_TYPE_VENDOR
+ | usb.util.CTRL_RECIPIENT_DEVICE,
+ 0,
+ wvalue,
+ windex,
+ length,
+ self.TIMEOUT,
+ )
+ else:
+ raise ValueError("Unknown status code: {}".format(response[0]))
+ time.sleep(0.01)
+
+ logging.info(
+ "ReadCMD: cmdid: {}, resid: {}, response: {}".format(
+ wvalue, windex, response
+ )
+ )
+
+ if data_type == "uint8":
+ result = response.tolist()
+ elif data_type == "char":
+ # For char arrays, convert bytes to string
+ byte_data = response.tobytes()
+ # Remove status byte and null terminators
+ result = byte_data[1:].rstrip(b"\x00").decode("utf-8", errors="ignore")
+ elif data_type == "radians" or data_type == "float":
+ byte_data = response.tobytes()
+ match_str = "<"
+ for i in range(data_cnt):
+ match_str += "f"
+ result = struct.unpack(match_str, byte_data[1:])
+ elif data_type == "uint32" or data_type == "int32":
+ result = response.tolist()
+
+ return result
+
+ def close(self) -> None:
+ """Close the interface."""
+ usb.util.dispose_resources(self.dev)
+
+
+def find(vid: int = 0x2886, pid: int = 0x001A) -> ReSpeaker | None:
+ """Find and return the ReSpeaker USB device with the given Vendor ID and Product ID."""
+ dev = usb.core.find(idVendor=vid, idProduct=pid)
+ if not dev:
+ return None
+
+ return ReSpeaker(dev)
+
+
+def init_respeaker_usb() -> Optional[ReSpeaker]:
+ """Initialize the ReSpeaker USB device. Looks for both new and beta device IDs."""
+ try:
+ dev = usb.core.find(
+ idVendor=0x38FB, idProduct=0x1001, backend=get_libusb1_backend()
+ )
+ if dev is None:
+ dev = usb.core.find(
+ idVendor=0x2886, idProduct=0x001A, backend=get_libusb1_backend()
+ )
+ if dev is None:
+ logging.error("No ReSpeaker USB device found !")
+ return None
+ else:
+ logging.warning("Old firmware detected on ReSpeaker USB device. Please update the firmware!")
+ return ReSpeaker(dev)
+ except usb.core.NoBackendError:
+ logging.error(
+ "No USB backend was found ! Make sure libusb_package is correctly installed with `pip install libusb_package`."
+ )
+ return None
+
+
+def main() -> None:
+ """Parse arguments and execute read/write commands."""
+ parser = argparse.ArgumentParser(
+ description="Reachy Mini Audio Host Control Script"
+ )
+ parser.add_argument(
+ "command",
+ choices=PARAMETERS.keys(),
+ help="Command to execute (e.g., VERSION, DOA_VALUE, etc.)",
+ )
+ parser.add_argument(
+ "--vid",
+ type=lambda x: int(x, 0),
+ default=0x38FB,
+ help="Vendor ID (default: 0x38FB)",
+ )
+ parser.add_argument(
+ "--pid",
+ type=lambda x: int(x, 0),
+ default=0x1001,
+ help="Product ID (default: 0x1001)",
+ )
+ parser.add_argument(
+ "--values",
+ nargs="+",
+ type=float,
+ help="Values for write commands (only for write operations)",
+ )
+
+ args = parser.parse_args()
+
+ dev = find(vid=args.vid, pid=args.pid)
+ if not dev:
+ print("No device found")
+ sys.exit(1)
+
+ try:
+ if args.values:
+ if PARAMETERS[args.command][3] == "ro":
+ print(f"Error: {args.command} is read-only and cannot be written to")
+ sys.exit(1)
+
+ if (
+ PARAMETERS[args.command][4] != "float"
+ and PARAMETERS[args.command][4] != "radians"
+ ):
+ args.values = [int(v) for v in args.values]
+
+ if PARAMETERS[args.command][2] != len(args.values):
+ print(
+ f"Error: {args.command} value count is {PARAMETERS[args.command][2]}, but {len(args.values)} values provided"
+ )
+ sys.exit(1)
+
+ print(f"Writing to {args.command} with values: {args.values}")
+ dev.write(args.command, args.values)
+ time.sleep(0.1)
+ print("Write operation completed successfully")
+ else:
+ if PARAMETERS[args.command][3] == "wo":
+ print(f"Error: {args.command} is write-only and cannot be read")
+ sys.exit(1)
+
+ result = dev.read(args.command)
+ print(f"{args.command}: {result}")
+
+ except Exception as e:
+ print(f"Error executing command {args.command}: {e}")
+ sys.exit(1)
+ finally:
+ dev.close()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/src/reachy_mini/media/audio_gstreamer.py b/reachy_mini/src/reachy_mini/media/audio_gstreamer.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a0f06158fb5dcf35af744e6055aaa338d1af4f2
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/audio_gstreamer.py
@@ -0,0 +1,200 @@
+"""GStreamer camera backend.
+
+This module provides an implementation of the CameraBase class using GStreamer.
+By default the module directly returns JPEG images as output by the camera.
+"""
+
+from threading import Thread
+from typing import Optional
+
+import numpy as np
+import numpy.typing as npt
+
+try:
+ import gi
+except ImportError as e:
+ raise ImportError(
+ "The 'gi' module is required for GStreamerCamera but could not be imported. \
+ Please install the GStreamer backend: pip install .[gstreamer]."
+ ) from e
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GstApp", "1.0")
+
+
+from gi.repository import GLib, Gst, GstApp # noqa: E402
+
+from .audio_base import AudioBase # noqa: E402
+
+
+class GStreamerAudio(AudioBase):
+ """Audio implementation using GStreamer."""
+
+ def __init__(self, log_level: str = "INFO") -> None:
+ """Initialize the GStreamer audio."""
+ super().__init__(log_level=log_level)
+ Gst.init(None)
+ self._loop = GLib.MainLoop()
+ self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
+ self._thread_bus_calls.start()
+
+ self._pipeline_record = Gst.Pipeline.new("audio_recorder")
+ self._appsink_audio: Optional[GstApp] = None
+ self._init_pipeline_record(self._pipeline_record)
+ self._bus_record = self._pipeline_record.get_bus()
+ self._bus_record.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+
+ self._pipeline_playback = Gst.Pipeline.new("audio_player")
+ self._appsrc: Optional[GstApp] = None
+ self._init_pipeline_playback(self._pipeline_playback)
+ self._bus_playback = self._pipeline_playback.get_bus()
+ self._bus_playback.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+
+ def _init_pipeline_record(self, pipeline: Gst.Pipeline) -> None:
+ self._appsink_audio = Gst.ElementFactory.make("appsink")
+ caps = Gst.Caps.from_string(
+ f"audio/x-raw,rate={self.SAMPLE_RATE},format=F32LE,layout=interleaved"
+ )
+ self._appsink_audio.set_property("caps", caps)
+ self._appsink_audio.set_property("drop", True) # avoid overflow
+ self._appsink_audio.set_property("max-buffers", 500)
+
+ autoaudiosrc = Gst.ElementFactory.make("autoaudiosrc") # use default mic
+
+ queue = Gst.ElementFactory.make("queue")
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+
+ if not all(
+ [autoaudiosrc, queue, audioconvert, audioresample, self._appsink_audio]
+ ):
+ raise RuntimeError("Failed to create GStreamer elements")
+
+ pipeline.add(autoaudiosrc)
+ pipeline.add(queue)
+ pipeline.add(audioconvert)
+ pipeline.add(audioresample)
+ pipeline.add(self._appsink_audio)
+
+ autoaudiosrc.link(queue)
+ queue.link(audioconvert)
+ audioconvert.link(audioresample)
+ audioresample.link(self._appsink_audio)
+
+ def __del__(self) -> None:
+ """Destructor to ensure gstreamer resources are released."""
+ super().__del__()
+ self._loop.quit()
+ self._bus_record.remove_watch()
+ self._bus_playback.remove_watch()
+
+ def _init_pipeline_playback(self, pipeline: Gst.Pipeline) -> None:
+ self._appsrc = Gst.ElementFactory.make("appsrc")
+ self._appsrc.set_property("format", Gst.Format.TIME)
+ self._appsrc.set_property("is-live", True)
+ caps = Gst.Caps.from_string(
+ f"audio/x-raw,format=F32LE,channels=1,rate={self.SAMPLE_RATE},layout=interleaved"
+ )
+ self._appsrc.set_property("caps", caps)
+
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+
+ queue = Gst.ElementFactory.make("queue")
+ audiosink = Gst.ElementFactory.make("autoaudiosink") # use default speaker
+
+ pipeline.add(queue)
+ pipeline.add(audiosink)
+ pipeline.add(self._appsrc)
+ pipeline.add(audioconvert)
+ pipeline.add(audioresample)
+
+ self._appsrc.link(queue)
+ queue.link(audioconvert)
+ audioconvert.link(audioresample)
+ audioresample.link(audiosink)
+
+ def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
+ t = msg.type
+ if t == Gst.MessageType.EOS:
+ self.logger.warning("End-of-stream")
+ return False
+
+ elif t == Gst.MessageType.ERROR:
+ err, debug = msg.parse_error()
+ self.logger.error(f"Error: {err} {debug}")
+ return False
+
+ return True
+
+ def start_recording(self) -> None:
+ """Open the audio card using GStreamer."""
+ self._pipeline_record.set_state(Gst.State.PLAYING)
+
+ def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
+ sample = appsink.try_pull_sample(20_000_000)
+ if sample is None:
+ return None
+ data = None
+ if isinstance(sample, Gst.Sample):
+ buf = sample.get_buffer()
+ if buf is None:
+ self.logger.warning("Buffer is None")
+
+ data = buf.extract_dup(0, buf.get_size())
+ return data
+
+ def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
+ """Read a sample from the audio card. Returns the sample or None if error.
+
+ Returns:
+ Optional[npt.NDArray[np.float32]]: The captured sample in raw format, or None if error.
+
+ """
+ sample = self._get_sample(self._appsink_audio)
+ if sample is None:
+ return None
+ return np.frombuffer(sample, dtype=np.float32).reshape(-1, 2)
+
+ def get_input_audio_samplerate(self) -> int:
+ """Get the input samplerate of the audio device."""
+ return self.SAMPLE_RATE
+
+ def get_output_audio_samplerate(self) -> int:
+ """Get the output samplerate of the audio device."""
+ return self.SAMPLE_RATE
+
+ def stop_recording(self) -> None:
+ """Release the camera resource."""
+ self._pipeline_record.set_state(Gst.State.NULL)
+
+ def start_playing(self) -> None:
+ """Open the audio output using GStreamer."""
+ self._pipeline_playback.set_state(Gst.State.PLAYING)
+
+ def stop_playing(self) -> None:
+ """Stop playing audio and release resources."""
+ self._pipeline_playback.set_state(Gst.State.NULL)
+
+ def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
+ """Push audio data to the output device."""
+ if self._appsrc is not None:
+ buf = Gst.Buffer.new_wrapped(data.tobytes())
+ self._appsrc.push_buffer(buf)
+ else:
+ self.logger.warning(
+ "AppSrc is not initialized. Call start_playing() first."
+ )
+
+ def play_sound(self, sound_file: str) -> None:
+ """Play a sound file.
+
+ Args:
+ sound_file (str): Path to the sound file to play.
+
+ """
+ self.logger.warning("play_sound is not implemented for GStreamerAudio.")
diff --git a/reachy_mini/src/reachy_mini/media/audio_sounddevice.py b/reachy_mini/src/reachy_mini/media/audio_sounddevice.py
new file mode 100644
index 0000000000000000000000000000000000000000..e381c685cf2e4e7cbb6be071b6757fe14ed71af9
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/audio_sounddevice.py
@@ -0,0 +1,242 @@
+"""Audio implementation using sounddevice backend."""
+
+import os
+import threading
+from typing import Any, List, Optional
+
+import numpy as np
+import numpy.typing as npt
+import scipy
+import sounddevice as sd
+import soundfile as sf
+
+from reachy_mini.utils.constants import ASSETS_ROOT_PATH
+
+from .audio_base import AudioBase
+
+
+class SoundDeviceAudio(AudioBase):
+ """Audio device implementation using sounddevice."""
+
+ def __init__(
+ self,
+ frames_per_buffer: int = 256,
+ log_level: str = "INFO",
+ ) -> None:
+ """Initialize the SoundDevice audio device."""
+ super().__init__(log_level=log_level)
+ self.frames_per_buffer = frames_per_buffer
+ self.stream = None
+ self._output_stream = None
+ self._buffer: List[npt.NDArray[np.float32]] = []
+ self._output_device_id = self._get_device_id(
+ ["Reachy Mini Audio", "respeaker"], device_io_type="output"
+ )
+ self._input_device_id = self._get_device_id(
+ ["Reachy Mini Audio", "respeaker"], device_io_type="input"
+ )
+
+ def start_recording(self) -> None:
+ """Open the audio input stream, using ReSpeaker card if available."""
+ self.stream = sd.InputStream(
+ blocksize=self.frames_per_buffer,
+ device=self._input_device_id,
+ callback=self._callback,
+ samplerate=self.get_input_audio_samplerate(),
+ )
+ if self.stream is None:
+ raise RuntimeError("Failed to open SoundDevice audio stream.")
+ self._buffer.clear()
+ self.stream.start()
+ self.logger.info("SoundDevice audio stream opened.")
+
+ def _callback(
+ self,
+ indata: npt.NDArray[np.float32],
+ frames: int,
+ time: int,
+ status: sd.CallbackFlags,
+ ) -> None:
+ if status:
+ self.logger.warning(f"SoundDevice status: {status}")
+
+ self._buffer.append(indata.copy())
+
+ def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
+ """Read audio data from the buffer. Returns numpy array or None if empty."""
+ if self._buffer and len(self._buffer) > 0:
+ data: npt.NDArray[np.float32] = np.concatenate(self._buffer, axis=0)
+ self._buffer.clear()
+ return data
+ self.logger.debug("No audio data available in buffer.")
+ return None
+
+ def get_input_audio_samplerate(self) -> int:
+ """Get the input samplerate of the audio device."""
+ return int(
+ sd.query_devices(self._input_device_id, "input")["default_samplerate"]
+ )
+
+ def get_output_audio_samplerate(self) -> int:
+ """Get the output samplerate of the audio device."""
+ return int(
+ sd.query_devices(self._output_device_id, "output")["default_samplerate"]
+ )
+
+ def stop_recording(self) -> None:
+ """Close the audio stream and release resources."""
+ if self.stream is not None:
+ self.stream.stop()
+ self.stream.close()
+ self.stream = None
+ self.logger.info("SoundDevice audio stream closed.")
+
+ def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
+ """Push audio data to the output device."""
+ if self._output_stream is not None:
+ if data.ndim > 1: # convert to mono
+ data = np.mean(data, axis=1)
+ self._output_stream.write(data)
+ else:
+ self.logger.warning(
+ "Output stream is not open. Call start_playing() first."
+ )
+
+ def start_playing(self) -> None:
+ """Open the audio output stream."""
+ if self._output_stream is not None:
+ self.stop_playing()
+ self._output_stream = sd.OutputStream(
+ samplerate=self.get_output_audio_samplerate(),
+ device=self._output_device_id,
+ channels=1,
+ )
+ if self._output_stream is None:
+ raise RuntimeError("Failed to open SoundDevice audio output stream.")
+ self._output_stream.start()
+
+ def stop_playing(self) -> None:
+ """Close the audio output stream."""
+ if self._output_stream is not None:
+ self._output_stream.stop()
+ self._output_stream.close()
+ self._output_stream = None
+ self.logger.info("SoundDevice audio output stream closed.")
+
+ def play_sound(self, sound_file: str, autoclean: bool = False) -> None:
+ """Play a sound file.
+
+ Args:
+ sound_file (str): Path to the sound file to play. May be given relative to the assets directory or as an absolute path.
+ autoclean (bool): If True, the audio device will be released after the sound is played.
+
+ """
+ if not os.path.exists(sound_file):
+ file_path = f"{ASSETS_ROOT_PATH}/{sound_file}"
+ if not os.path.exists(file_path):
+ raise FileNotFoundError(
+ f"Sound file {sound_file} not found in assets directory or given path."
+ )
+ else:
+ file_path = sound_file
+
+ data, samplerate_in = sf.read(file_path, dtype="float32")
+ samplerate_out = self.get_output_audio_samplerate()
+
+ if samplerate_in != samplerate_out:
+ data = scipy.signal.resample(
+ data, int(len(data) * (samplerate_out / samplerate_in))
+ )
+ if data.ndim > 1: # convert to mono
+ data = np.mean(data, axis=1)
+
+ self.logger.debug(f"Playing sound '{file_path}' at {samplerate_in} Hz")
+
+ self.stop_playing()
+ start = 0 # current position in audio data
+ length = len(data)
+
+ def callback(
+ outdata: npt.NDArray[np.float32],
+ frames: int,
+ time: Any, # cdata 'struct PaStreamCallbackTimeInfo *
+ status: sd.CallbackFlags,
+ ) -> None:
+ """Actual playback."""
+ nonlocal start
+
+ if status:
+ self.logger.warning(f"SoundDevice output status: {status}")
+
+ end = start + frames
+ if end > length:
+ # Fill the output buffer with the audio data, or zeros if finished
+ outdata[: length - start, 0] = data[start:]
+ outdata[length - start :, 0] = 0
+ raise sd.CallbackStop()
+ else:
+ outdata[:, 0] = data[start:end]
+ start = end
+
+ stop_event = threading.Event()
+
+ self._output_stream = sd.OutputStream(
+ samplerate=samplerate_out,
+ device=self._output_device_id,
+ channels=1,
+ callback=callback,
+ finished_callback=stop_event.set, # release the device when done
+ )
+ if self._output_stream is None:
+ raise RuntimeError("Failed to open SoundDevice audio output stream.")
+ self._output_stream.start()
+
+ if autoclean:
+
+ def _clean_up_thread() -> None:
+ stop_event.wait()
+ self.stop_playing()
+
+ threading.Thread(
+ target=_clean_up_thread,
+ daemon=True,
+ ).start()
+
+ def _get_device_id(
+ self, names_contains: List[str], device_io_type: str = "output"
+ ) -> int:
+ """Return the output device id whose name contains the given strings (case-insensitive).
+
+ Args:
+ names_contains (List[str]): List of strings that should be contained in the device name.
+ device_io_type (str): 'input' or 'output' to specify device type.
+
+ If not found, return the default output device id.
+
+ """
+ devices = sd.query_devices()
+
+ for idx, dev in enumerate(devices):
+ for name_contains in names_contains:
+ if (
+ name_contains.lower() in dev["name"].lower()
+ and dev[f"max_{device_io_type}_channels"] > 0
+ ):
+ return idx
+ # Return default output device if not found
+ self.logger.warning(
+ f"No {device_io_type} device found containing '{names_contains}', using default."
+ )
+ return self._safe_query_device(device_io_type)
+
+ def _safe_query_device(self, kind: str) -> int:
+ try:
+ return int(sd.query_devices(None, kind)["index"])
+ except sd.PortAudioError:
+ return (
+ int(sd.default.device[1])
+ if kind == "input"
+ else int(sd.default.device[0])
+ )
+ except IndexError:
+ return 0
diff --git a/reachy_mini/src/reachy_mini/media/audio_utils.py b/reachy_mini/src/reachy_mini/media/audio_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..53dbbf17564d49efe626fb0b9f318f5b3ce58952
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/audio_utils.py
@@ -0,0 +1,35 @@
+"""Utility functions for audio handling, specifically for detecting the ReSpeaker sound card."""
+
+import logging
+import subprocess
+
+
+def get_respeaker_card_number() -> int:
+ """Return the card number of the ReSpeaker sound card, or 0 if not found."""
+ try:
+ result = subprocess.run(
+ ["arecord", "-l"], capture_output=True, text=True, check=True
+ )
+ output = result.stdout
+
+ lines = output.split("\n")
+ for line in lines:
+ if "reachy mini audio" in line.lower() and "card" in line:
+ card_number = line.split(":")[0].split("card ")[1].strip()
+ logging.debug(f"Found Reachy Mini Audio sound card: {card_number}")
+ return int(card_number)
+ elif "respeaker" in line.lower() and "card" in line:
+ card_number = line.split(":")[0].split("card ")[1].strip()
+ logging.warning(
+ f"Found ReSpeaker sound card: {card_number}. Please update firmware!"
+ )
+ return int(card_number)
+
+ logging.warning(
+ "Reachy Mini Audio sound card not found. Returning default card"
+ )
+ return 0 # default sound card
+
+ except subprocess.CalledProcessError as e:
+ logging.error(f"Cannot find sound card: {e}")
+ return 0
diff --git a/reachy_mini/src/reachy_mini/media/camera_base.py b/reachy_mini/src/reachy_mini/media/camera_base.py
new file mode 100644
index 0000000000000000000000000000000000000000..53b4a5b6252c4417aa1852c990d8197178d0af32
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/camera_base.py
@@ -0,0 +1,100 @@
+"""Base classes for camera implementations.
+
+The camera implementations support various backends and provide a unified
+interface for capturing images.
+"""
+
+import logging
+from abc import ABC, abstractmethod
+from typing import Optional
+
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.camera_constants import (
+ CameraResolution,
+ CameraSpecs,
+ MujocoCameraSpecs,
+)
+
+
+class CameraBase(ABC):
+ """Abstract class for opening and managing a camera."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ ) -> None:
+ """Initialize the camera."""
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+ self._resolution: Optional[CameraResolution] = None
+ self.camera_specs: Optional[CameraSpecs] = None
+ self.resized_K: Optional[npt.NDArray[np.float64]] = None
+
+ @property
+ def resolution(self) -> tuple[int, int]:
+ """Get the current camera resolution as a tuple (width, height)."""
+ if self._resolution is None:
+ raise RuntimeError("Camera resolution is not set.")
+ return (self._resolution.value[0], self._resolution.value[1])
+
+ @property
+ def framerate(self) -> int:
+ """Get the current camera frames per second."""
+ if self._resolution is None:
+ raise RuntimeError("Camera resolution is not set.")
+ return int(self._resolution.value[2])
+
+ @property
+ def K(self) -> Optional[npt.NDArray[np.float64]]:
+ """Get the camera intrinsic matrix for the current resolution."""
+ return self.resized_K
+
+ @property
+ def D(self) -> Optional[npt.NDArray[np.float64]]:
+ """Get the camera distortion coefficients."""
+ if self.camera_specs is not None:
+ return self.camera_specs.D
+ return None
+
+ def set_resolution(self, resolution: CameraResolution) -> None:
+ """Set the camera resolution."""
+ if self.camera_specs is None:
+ raise RuntimeError(
+ "Camera specs not set. Open the camera before setting the resolution."
+ )
+
+ if isinstance(self.camera_specs, MujocoCameraSpecs):
+ raise RuntimeError(
+ "Cannot change resolution of Mujoco simulated camera for now."
+ )
+
+ if resolution not in self.camera_specs.available_resolutions:
+ raise ValueError(
+ f"Resolution not supported by the camera. Available resolutions are : {self.camera_specs.available_resolutions}"
+ )
+
+ w_ratio = resolution.value[0] / self.camera_specs.default_resolution.value[0]
+ h_ratio = resolution.value[1] / self.camera_specs.default_resolution.value[1]
+ self.resized_K = self.camera_specs.K.copy()
+
+ self.resized_K[0, 0] *= w_ratio
+ self.resized_K[1, 1] *= h_ratio
+ self.resized_K[0, 2] *= w_ratio
+ self.resized_K[1, 2] *= h_ratio
+
+ @abstractmethod
+ def open(self) -> None:
+ """Open the camera."""
+ pass
+
+ @abstractmethod
+ def read(self) -> Optional[npt.NDArray[np.uint8]]:
+ """Read an image from the camera. Returns the image or None if error."""
+ pass
+
+ @abstractmethod
+ def close(self) -> None:
+ """Close the camera and release resources."""
+ pass
diff --git a/reachy_mini/src/reachy_mini/media/camera_constants.py b/reachy_mini/src/reachy_mini/media/camera_constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..d3ba0f471e83def0c786ca18389ceac725809fe8
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/camera_constants.py
@@ -0,0 +1,163 @@
+"""Camera constants for Reachy Mini."""
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import List
+
+import numpy as np
+import numpy.typing as npt
+
+
+class CameraResolution(Enum):
+ """Base class for camera resolutions."""
+
+ pass
+
+
+class ArduCamResolution(CameraResolution):
+ """Camera resolutions. Arducam_12MP."""
+
+ R2304x1296 = (2304, 1296, 30)
+ R4608x2592 = (4608, 2592, 10)
+ R1920x1080 = (1920, 1080, 30)
+ R1600x1200 = (1600, 1200, 30)
+ R1280x720 = (1280, 720, 30)
+
+
+class RPICameraResolution(CameraResolution):
+ """Camera resolutions. Raspberry Pi Camera.
+
+ Camera supports higher resolutions but the h264 encoder won't follow.
+ """
+
+ R1920x1080 = (1920, 1080, 60)
+ R1600x1200 = (1600, 1200, 30)
+ R1536x864 = (1536, 864, 40)
+ R1280x720 = (1280, 720, 60)
+
+
+class MujocoCameraResolution(CameraResolution):
+ """Camera resolutions for Mujoco simulated camera."""
+
+ R1280x720 = (1280, 720, 60)
+
+
+@dataclass
+class CameraSpecs:
+ """Base camera specifications."""
+
+ available_resolutions: List[CameraResolution] = field(default_factory=list)
+ default_resolution: CameraResolution = ArduCamResolution.R1280x720
+ vid = 0
+ pid = 0
+ K: npt.NDArray[np.float64] = field(default_factory=lambda: np.eye(3))
+ D: npt.NDArray[np.float64] = field(default_factory=lambda: np.zeros((5,)))
+
+
+@dataclass
+class ArducamSpecs(CameraSpecs):
+ """Arducam camera specifications."""
+
+ available_resolutions = [
+ ArduCamResolution.R2304x1296,
+ ArduCamResolution.R4608x2592,
+ ArduCamResolution.R1920x1080,
+ ArduCamResolution.R1600x1200,
+ ArduCamResolution.R1280x720,
+ ]
+ default_resolution = ArduCamResolution.R1280x720
+ vid = 0x0C45
+ pid = 0x636D
+ K = np.array([[550.3564, 0.0, 638.0112], [0.0, 549.1653, 364.589], [0.0, 0.0, 1.0]])
+ D = np.array([-0.0694, 0.1565, -0.0004, 0.0003, -0.0983])
+
+
+@dataclass
+class ReachyMiniCamSpecs(CameraSpecs):
+ """Reachy Mini camera specifications."""
+
+ available_resolutions = [
+ RPICameraResolution.R1920x1080,
+ RPICameraResolution.R1600x1200,
+ RPICameraResolution.R1536x864,
+ RPICameraResolution.R1280x720,
+ ]
+ default_resolution = RPICameraResolution.R1920x1080
+ vid = 0x38FB
+ pid = 0x1002
+ K = np.array(
+ [
+ [821.515, 0.0, 962.241],
+ [0.0, 820.830, 542.459],
+ [0.0, 0.0, 1.0],
+ ]
+ )
+
+ D = np.array(
+ [
+ -2.94475669e-02,
+ 6.00511974e-02,
+ 3.57813971e-06,
+ -2.96459394e-04,
+ -3.79243988e-02,
+ ]
+ )
+
+
+@dataclass
+class OlderRPiCamSpecs(CameraSpecs):
+ """Older Raspberry Pi camera specifications."""
+
+ available_resolutions = [
+ RPICameraResolution.R1920x1080,
+ RPICameraResolution.R1600x1200,
+ RPICameraResolution.R1536x864,
+ RPICameraResolution.R1280x720,
+ ]
+ default_resolution = RPICameraResolution.R1920x1080
+ vid = 0x1BCF
+ pid = 0x28C4
+ K = np.array(
+ [
+ [821.51459423, 0.0, 962.24086301],
+ [0.0, 820.82987265, 542.45854246],
+ [0.0, 0.0, 1.0],
+ ]
+ )
+
+ D = np.array(
+ [
+ -2.94475669e-02,
+ 6.00511974e-02,
+ 3.57813971e-06,
+ -2.96459394e-04,
+ -3.79243988e-02,
+ ]
+ )
+
+
+@dataclass
+class MujocoCameraSpecs(CameraSpecs):
+ """Mujoco simulated camera specifications."""
+
+ available_resolutions = [
+ MujocoCameraResolution.R1280x720,
+ ]
+ default_resolution = MujocoCameraResolution.R1280x720
+ # ideal camera matrix
+ K = np.array(
+ [
+ [
+ MujocoCameraResolution.R1280x720.value[0],
+ 0.0,
+ MujocoCameraResolution.R1280x720.value[0] / 2,
+ ],
+ [
+ 0.0,
+ MujocoCameraResolution.R1280x720.value[1],
+ MujocoCameraResolution.R1280x720.value[1] / 2,
+ ],
+ [0.0, 0.0, 1.0],
+ ]
+ )
+ D = np.zeros((5,)) # no distortion
diff --git a/reachy_mini/src/reachy_mini/media/camera_gstreamer.py b/reachy_mini/src/reachy_mini/media/camera_gstreamer.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc70a478c6a7e45dec2abc21c574c01fed1ff59c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/camera_gstreamer.py
@@ -0,0 +1,196 @@
+"""GStreamer camera backend.
+
+This module provides an implementation of the CameraBase class using GStreamer.
+By default the module directly returns JPEG images as output by the camera.
+"""
+
+from threading import Thread
+from typing import Optional, cast
+
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.camera_constants import (
+ ArducamSpecs,
+ CameraResolution,
+ CameraSpecs,
+ ReachyMiniCamSpecs,
+)
+
+try:
+ import gi
+except ImportError as e:
+ raise ImportError(
+ "The 'gi' module is required for GStreamerCamera but could not be imported. \
+ Please install the GStreamer backend: pip install .[gstreamer]."
+ ) from e
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GstApp", "1.0")
+
+
+from gi.repository import GLib, Gst, GstApp # noqa: E402
+
+from .camera_base import CameraBase # noqa: E402
+
+
+class GStreamerCamera(CameraBase):
+ """Camera implementation using GStreamer."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ ) -> None:
+ """Initialize the GStreamer camera."""
+ super().__init__(log_level=log_level)
+ Gst.init(None)
+ self._loop = GLib.MainLoop()
+ self._thread_bus_calls: Optional[Thread] = None
+
+ self.pipeline = Gst.Pipeline.new("camera_recorder")
+
+ # TODO How do we hande video device not found ?
+ cam_path = self.get_video_device()
+ if self.camera_specs is None:
+ raise RuntimeError("Camera specs not set")
+ self._resolution = self.camera_specs.default_resolution
+
+ if self._resolution is None:
+ raise RuntimeError("Failed to get default camera resolution.")
+
+ # note for some applications the jpeg image could be directly used
+ self._appsink_video: GstApp = Gst.ElementFactory.make("appsink")
+ self.set_resolution(self._resolution)
+ self._appsink_video.set_property("drop", True) # avoid overflow
+ self._appsink_video.set_property("max-buffers", 1) # keep last image only
+ self.pipeline.add(self._appsink_video)
+
+ # cam_path = self.get_video_device()
+ if cam_path == "":
+ self.logger.warning("Recording pipeline set without camera.")
+ self.pipeline.remove(self._appsink_video)
+ else:
+ camsrc = Gst.ElementFactory.make("v4l2src")
+ camsrc.set_property("device", cam_path)
+ self.pipeline.add(camsrc)
+ queue = Gst.ElementFactory.make("queue")
+ self.pipeline.add(queue)
+ # use vaapijpegdec or nvjpegdec for hardware acceleration
+ jpegdec = Gst.ElementFactory.make("jpegdec")
+ self.pipeline.add(jpegdec)
+ videoconvert = Gst.ElementFactory.make("videoconvert")
+ self.pipeline.add(videoconvert)
+ camsrc.link(queue)
+ queue.link(jpegdec)
+ jpegdec.link(videoconvert)
+ videoconvert.link(self._appsink_video)
+
+ def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
+ t = msg.type
+ if t == Gst.MessageType.EOS:
+ self.logger.warning("End-of-stream")
+ return False
+
+ elif t == Gst.MessageType.ERROR:
+ err, debug = msg.parse_error()
+ self.logger.error(f"Error: {err} {debug}")
+ return False
+
+ return True
+
+ def _handle_bus_calls(self) -> None:
+ self.logger.debug("starting bus message loop")
+ bus = self.pipeline.get_bus()
+ bus.add_watch(GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop)
+ self._loop.run()
+ bus.remove_watch()
+ self.logger.debug("bus message loop stopped")
+
+ def set_resolution(self, resolution: CameraResolution) -> None:
+ """Set the camera resolution."""
+ super().set_resolution(resolution)
+
+ # Check if pipeline is not playing before changing resolution
+ if self.pipeline.get_state(0).state == Gst.State.PLAYING:
+ raise RuntimeError(
+ "Cannot change resolution while the camera is streaming. Please close the camera first."
+ )
+
+ self._resolution = resolution
+ caps_video = Gst.Caps.from_string(
+ f"video/x-raw,format=BGR, width={self._resolution.value[0]},height={self._resolution.value[1]},framerate={self.framerate}/1"
+ )
+ self._appsink_video.set_property("caps", caps_video)
+
+ def open(self) -> None:
+ """Open the camera using GStreamer."""
+ self.pipeline.set_state(Gst.State.PLAYING)
+ self._thread_bus_calls = Thread(target=self._handle_bus_calls, daemon=True)
+ self._thread_bus_calls.start()
+
+ def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
+ sample = appsink.try_pull_sample(20_000_000)
+ if sample is None:
+ return None
+ data = None
+ if isinstance(sample, Gst.Sample):
+ buf = sample.get_buffer()
+ if buf is None:
+ self.logger.warning("Buffer is None")
+
+ data = buf.extract_dup(0, buf.get_size())
+ return data
+
+ def read(self) -> Optional[npt.NDArray[np.uint8]]:
+ """Read a frame from the camera. Returns the frame or None if error.
+
+ Returns:
+ Optional[npt.NDArray[np.uint8]]: The captured BGR frame as a NumPy array, or None if error.
+
+ """
+ data = self._get_sample(self._appsink_video)
+ if data is None:
+ return None
+
+ arr = np.frombuffer(data, dtype=np.uint8).reshape(
+ (self.resolution[1], self.resolution[0], 3)
+ )
+ return arr
+
+ def close(self) -> None:
+ """Release the camera resource."""
+ self._loop.quit()
+ self.pipeline.set_state(Gst.State.NULL)
+
+ def get_video_device(self) -> str:
+ """Use Gst.DeviceMonitor to find the unix camera path /dev/videoX.
+
+ Returns the device path (e.g., '/dev/video2'), or '' if not found.
+ """
+ monitor = Gst.DeviceMonitor()
+ monitor.add_filter("Video/Source")
+ monitor.start()
+
+ cam_names = ["Reachy", "Arducam_12MP"]
+
+ devices = monitor.get_devices()
+ for cam_name in cam_names:
+ for device in devices:
+ name = device.get_display_name()
+ device_props = device.get_properties()
+
+ if cam_name in name:
+ if device_props and device_props.has_field("api.v4l2.path"):
+ device_path = device_props.get_string("api.v4l2.path")
+ self.camera_specs = (
+ cast(CameraSpecs, ArducamSpecs)
+ if cam_name == "Arducam_12MP"
+ else cast(CameraSpecs, ReachyMiniCamSpecs)
+ )
+ self.resized_K = self.camera_specs.K
+ self.logger.debug(f"Found {cam_name} camera at {device_path}")
+ monitor.stop()
+ return str(device_path)
+ monitor.stop()
+ self.logger.warning("No camera found.")
+ return ""
diff --git a/reachy_mini/src/reachy_mini/media/camera_opencv.py b/reachy_mini/src/reachy_mini/media/camera_opencv.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5c8d620bfd7768ed2f7bacdc9141c832d25501b
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/camera_opencv.py
@@ -0,0 +1,79 @@
+"""OpenCv camera backend.
+
+This module provides an implementation of the CameraBase class using OpenCV.
+"""
+
+from typing import Optional, cast
+
+import cv2
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.camera_constants import (
+ CameraResolution,
+ CameraSpecs,
+ MujocoCameraSpecs,
+)
+from reachy_mini.media.camera_utils import find_camera
+
+from .camera_base import CameraBase
+
+
+class OpenCVCamera(CameraBase):
+ """Camera implementation using OpenCV."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ ) -> None:
+ """Initialize the OpenCV camera."""
+ super().__init__(log_level=log_level)
+ self.cap: Optional[cv2.VideoCapture] = None
+
+ def set_resolution(self, resolution: CameraResolution) -> None:
+ """Set the camera resolution."""
+ super().set_resolution(resolution)
+
+ self._resolution = resolution
+ if self.cap is not None:
+ self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self._resolution.value[0])
+ self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self._resolution.value[1])
+
+ def open(self, udp_camera: Optional[str] = None) -> None:
+ """Open the camera using OpenCV VideoCapture."""
+ if udp_camera:
+ self.cap = cv2.VideoCapture(udp_camera)
+ self.camera_specs = cast(CameraSpecs, MujocoCameraSpecs)
+ self._resolution = self.camera_specs.default_resolution
+ else:
+ self.cap, self.camera_specs = find_camera()
+ if self.cap is None or self.camera_specs is None:
+ raise RuntimeError("Camera not found")
+
+ self._resolution = self.camera_specs.default_resolution
+ if self._resolution is None:
+ raise RuntimeError("Failed to get default camera resolution.")
+
+ self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self._resolution.value[0])
+ self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self._resolution.value[1])
+
+ self.resized_K = self.camera_specs.K
+
+ if not self.cap.isOpened():
+ raise RuntimeError("Failed to open camera")
+
+ def read(self) -> Optional[npt.NDArray[np.uint8]]:
+ """Read a frame from the camera. Returns the frame or None if error."""
+ if self.cap is None:
+ raise RuntimeError("Camera is not opened.")
+ ret, frame = self.cap.read()
+ if not ret:
+ return None
+
+ return np.asarray(frame, dtype=np.uint8, copy=False)
+
+ def close(self) -> None:
+ """Release the camera resource."""
+ if self.cap is not None:
+ self.cap.release()
+ self.cap = None
diff --git a/reachy_mini/src/reachy_mini/media/camera_utils.py b/reachy_mini/src/reachy_mini/media/camera_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfd1fc1161900cbf641c8e2d247fbebb2a0d2add
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/camera_utils.py
@@ -0,0 +1,111 @@
+"""Camera utility for Reachy Mini."""
+
+import platform
+from typing import Optional, Tuple, cast
+
+import cv2
+from cv2_enumerate_cameras import enumerate_cameras
+
+from reachy_mini.media.camera_constants import (
+ ArducamSpecs,
+ CameraSpecs,
+ OlderRPiCamSpecs,
+ ReachyMiniCamSpecs,
+)
+
+
+def find_camera(
+ apiPreference: int = cv2.CAP_ANY, no_cap: bool = False
+) -> Tuple[Optional[cv2.VideoCapture], Optional[CameraSpecs]]:
+ """Find and return the Reachy Mini camera.
+
+ Looks for the Reachy Mini camera first, then Arducam, then older Raspberry Pi Camera. Returns None if no camera is found.
+
+ Args:
+ apiPreference (int): Preferred API backend for the camera. Default is cv2.CAP_ANY.
+ no_cap (bool): If True, close the camera after finding it. Default is False.
+
+ Returns:
+ cv2.VideoCapture | None: A VideoCapture object if the camera is found and opened successfully, otherwise None.
+
+ """
+ cap = find_camera_by_vid_pid(
+ ReachyMiniCamSpecs.vid, ReachyMiniCamSpecs.pid, apiPreference
+ )
+ if cap is not None:
+ fourcc = cv2.VideoWriter_fourcc("M", "J", "P", "G") # type: ignore
+ cap.set(cv2.CAP_PROP_FOURCC, fourcc)
+ if no_cap:
+ cap.release()
+ return cap, cast(CameraSpecs, ReachyMiniCamSpecs)
+
+ cap = find_camera_by_vid_pid(
+ OlderRPiCamSpecs.vid, OlderRPiCamSpecs.pid, apiPreference
+ )
+ if cap is not None:
+ fourcc = cv2.VideoWriter_fourcc("M", "J", "P", "G") # type: ignore
+ cap.set(cv2.CAP_PROP_FOURCC, fourcc)
+ if no_cap:
+ cap.release()
+ return cap, cast(CameraSpecs, OlderRPiCamSpecs)
+
+ cap = find_camera_by_vid_pid(ArducamSpecs.vid, ArducamSpecs.pid, apiPreference)
+ if cap is not None:
+ if no_cap:
+ cap.release()
+ return cap, cast(CameraSpecs, ArducamSpecs)
+
+ return None, None
+
+
+def find_camera_by_vid_pid(
+ vid: int = ReachyMiniCamSpecs.vid,
+ pid: int = ReachyMiniCamSpecs.pid,
+ apiPreference: int = cv2.CAP_ANY,
+) -> cv2.VideoCapture | None:
+ """Find and return a camera with the specified VID and PID.
+
+ Args:
+ vid (int): Vendor ID of the camera. Default is ReachyMiniCamera
+ pid (int): Product ID of the camera. Default is ReachyMiniCamera
+ apiPreference (int): Preferred API backend for the camera. Default is cv2.CAP_ANY.
+
+ Returns:
+ cv2.VideoCapture | None: A VideoCapture object if the camera is found and opened successfully, otherwise None.
+
+ """
+ if platform.system() == "Linux":
+ apiPreference = cv2.CAP_V4L2
+
+ selected_cap = None
+ for c in enumerate_cameras(apiPreference):
+ if c.vid == vid and c.pid == pid:
+ # the Arducam camera creates two /dev/videoX devices
+ # that enumerate_cameras cannot differentiate
+ try:
+ cap = cv2.VideoCapture(c.index, c.backend)
+ if cap.isOpened():
+ selected_cap = cap
+ except Exception as e:
+ print(f"Error opening camera {c.index}: {e}")
+ return selected_cap
+
+
+if __name__ == "__main__":
+ from reachy_mini.media.camera_constants import ArduCamResolution
+
+ cam, _ = find_camera()
+ if cam is None:
+ exit("Camera not found")
+
+ cam.set(cv2.CAP_PROP_FRAME_WIDTH, ArduCamResolution.R1280x720.value[0])
+ cam.set(cv2.CAP_PROP_FRAME_HEIGHT, ArduCamResolution.R1280x720.value[1])
+
+ while True:
+ ret, frame = cam.read()
+ if not ret:
+ print("Failed to grab frame")
+ break
+ cv2.imshow("Camera Feed", frame)
+ if cv2.waitKey(1) & 0xFF == ord("q"):
+ break
diff --git a/reachy_mini/src/reachy_mini/media/media_manager.py b/reachy_mini/src/reachy_mini/media/media_manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..956edb3bc52419b1e273efc16ff8a399a63b5287
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/media_manager.py
@@ -0,0 +1,235 @@
+"""Media Manager.
+
+Provides camera and audio access based on the selected backedn
+"""
+
+import logging
+from enum import Enum
+from typing import Optional
+
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.audio_base import AudioBase
+from reachy_mini.media.camera_base import CameraBase
+
+# actual backends are dynamically imported
+
+
+class MediaBackend(Enum):
+ """Media backends."""
+
+ NO_MEDIA = "no_media"
+ DEFAULT = "default"
+ DEFAULT_NO_VIDEO = "default_no_video"
+ GSTREAMER = "gstreamer"
+ WEBRTC = "webrtc"
+
+
+class MediaManager:
+ """Abstract class for opening and managing audio devices."""
+
+ def __init__(
+ self,
+ backend: MediaBackend = MediaBackend.DEFAULT,
+ log_level: str = "INFO",
+ use_sim: bool = False,
+ signalling_host: str = "localhost",
+ ) -> None:
+ """Initialize the audio device."""
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+ self.backend = backend
+ self.camera: Optional[CameraBase] = None
+ self.audio: Optional[AudioBase] = None
+
+ match backend:
+ case MediaBackend.NO_MEDIA:
+ self.logger.info("No media backend selected.")
+ case MediaBackend.DEFAULT:
+ self.logger.info("Using default media backend (OpenCV + SoundDevice).")
+ self._init_camera(use_sim, log_level)
+ self._init_audio(log_level)
+ case MediaBackend.DEFAULT_NO_VIDEO:
+ self.logger.info("Using default media backend (SoundDevice only).")
+ self._init_audio(log_level)
+ case MediaBackend.GSTREAMER:
+ self.logger.info("Using GStreamer media backend.")
+ self._init_camera(use_sim, log_level)
+ self._init_audio(log_level)
+ case MediaBackend.WEBRTC:
+ self.logger.info("Using WebRTC GStreamer backend.")
+ self._init_webrtc(log_level, signalling_host, 8443)
+ case _:
+ raise NotImplementedError(f"Media backend {backend} not implemented.")
+
+ def close(self) -> None:
+ """Close the media manager and release resources."""
+ if self.camera is not None:
+ self.camera.close()
+ if self.audio is not None:
+ self.audio.stop_recording()
+ self.audio.stop_playing()
+
+ def __del__(self) -> None:
+ """Destructor to ensure resources are released."""
+ self.close()
+
+ def _init_camera(
+ self,
+ use_sim: bool,
+ log_level: str,
+ ) -> None:
+ """Initialize the camera."""
+ self.logger.debug("Initializing camera...")
+ if self.backend == MediaBackend.DEFAULT:
+ self.logger.info("Using OpenCV camera backend.")
+ from reachy_mini.media.camera_opencv import OpenCVCamera
+
+ self.camera = OpenCVCamera(log_level=log_level)
+ if use_sim:
+ self.camera.open(udp_camera="udp://@127.0.0.1:5005")
+ else:
+ self.camera.open()
+ elif self.backend == MediaBackend.GSTREAMER:
+ self.logger.info("Using GStreamer camera backend.")
+ from reachy_mini.media.camera_gstreamer import GStreamerCamera
+
+ self.camera = GStreamerCamera(log_level=log_level)
+ self.camera.open()
+ # Todo: use simulation with gstreamer?
+
+ else:
+ raise NotImplementedError(f"Camera backend {self.backend} not implemented.")
+
+ def get_frame(self) -> Optional[npt.NDArray[np.uint8]]:
+ """Get a frame from the camera.
+
+ Returns:
+ Optional[npt.NDArray[np.uint8]]: The captured BGR frame, or None if the camera is not available.
+
+ """
+ if self.camera is None:
+ self.logger.warning("Camera is not initialized.")
+ return None
+ return self.camera.read()
+
+ def _init_audio(self, log_level: str) -> None:
+ """Initialize the audio system."""
+ self.logger.debug("Initializing audio...")
+ if (
+ self.backend == MediaBackend.DEFAULT
+ or self.backend == MediaBackend.DEFAULT_NO_VIDEO
+ ):
+ self.logger.info("Using SoundDevice audio backend.")
+ from reachy_mini.media.audio_sounddevice import SoundDeviceAudio
+
+ self.audio = SoundDeviceAudio(log_level=log_level)
+ elif self.backend == MediaBackend.GSTREAMER:
+ self.logger.info("Using GStreamer audio backend.")
+ from reachy_mini.media.audio_gstreamer import GStreamerAudio
+
+ self.audio = GStreamerAudio(log_level=log_level)
+ else:
+ raise NotImplementedError(f"Audio backend {self.backend} not implemented.")
+
+ def _init_webrtc(
+ self, log_level: str, signalling_host: str, signalling_port: int
+ ) -> None:
+ """Initialize the WebRTC system (not implemented yet)."""
+ from gst_signalling.utils import find_producer_peer_id_by_name
+
+ from reachy_mini.media.webrtc_client_gstreamer import GstWebRTCClient
+
+ peer_id = find_producer_peer_id_by_name(
+ signalling_host, signalling_port, "reachymini"
+ )
+
+ webrtc_media: GstWebRTCClient = GstWebRTCClient(
+ log_level=log_level,
+ peer_id=peer_id,
+ signaling_host=signalling_host,
+ signaling_port=signalling_port,
+ )
+
+ self.camera = webrtc_media
+ self.audio = webrtc_media # GstWebRTCClient handles both audio and video
+ self.camera.open()
+
+ def play_sound(self, sound_file: str) -> None:
+ """Play a sound file.
+
+ Args:
+ sound_file (str): Path to the sound file to play.
+
+ """
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.play_sound(sound_file)
+
+ def start_recording(self) -> None:
+ """Start recording audio."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.start_recording()
+
+ def get_audio_sample(self) -> Optional[bytes | npt.NDArray[np.float32]]:
+ """Get an audio sample from the audio device.
+
+ Returns:
+ Optional[np.ndarray]: The recorded audio sample, or None if no data is available.
+
+ """
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return None
+ return self.audio.get_audio_sample()
+
+ def get_input_audio_samplerate(self) -> int:
+ """Get the input samplerate of the audio device."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return -1
+ return self.audio.get_input_audio_samplerate()
+
+ def get_output_audio_samplerate(self) -> int:
+ """Get the output samplerate of the audio device."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return -1
+ return self.audio.get_output_audio_samplerate()
+
+ def stop_recording(self) -> None:
+ """Stop recording audio."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.stop_recording()
+
+ def start_playing(self) -> None:
+ """Start playing audio."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.start_playing()
+
+ def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
+ """Push audio data to the output device.
+
+ Args:
+ data (npt.NDArray[np.float32]): The audio data to push to the output device (mono format).
+
+ """
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.push_audio_sample(data)
+
+ def stop_playing(self) -> None:
+ """Stop playing audio."""
+ if self.audio is None:
+ self.logger.warning("Audio system is not initialized.")
+ return
+ self.audio.stop_playing()
diff --git a/reachy_mini/src/reachy_mini/media/webrtc_client_gstreamer.py b/reachy_mini/src/reachy_mini/media/webrtc_client_gstreamer.py
new file mode 100644
index 0000000000000000000000000000000000000000..e67c5f05c1344f2d2a2b633ba3c28a2d7a098f24
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/webrtc_client_gstreamer.py
@@ -0,0 +1,289 @@
+"""GStreamer WebRTC client implementation.
+
+The class is a client for the webrtc server hosted on the Reachy Mini Wireless robot.
+"""
+
+from threading import Thread
+from typing import Optional, cast
+
+import gi
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.media.audio_base import AudioBase
+from reachy_mini.media.camera_base import CameraBase
+from reachy_mini.media.camera_constants import (
+ CameraResolution,
+ CameraSpecs,
+ ReachyMiniCamSpecs,
+ RPICameraResolution,
+)
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GstApp", "1.0")
+from gi.repository import GLib, Gst, GstApp # noqa: E402, F401
+
+
+class GstWebRTCClient(CameraBase, AudioBase):
+ """GStreamer WebRTC client implementation."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ peer_id: str = "",
+ signaling_host: str = "",
+ signaling_port: int = 8443,
+ ):
+ """Initialize the GStreamer WebRTC client."""
+ super().__init__(log_level=log_level)
+ Gst.init(None)
+ self._loop = GLib.MainLoop()
+ self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
+ self._thread_bus_calls.start()
+
+ self._pipeline_record = Gst.Pipeline.new("audio_recorder")
+ self._bus_record = self._pipeline_record.get_bus()
+ self._bus_record.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+
+ self._appsink_audio = Gst.ElementFactory.make("appsink")
+ caps = Gst.Caps.from_string(
+ f"audio/x-raw,rate={self.SAMPLE_RATE},format=F32LE,layout=interleaved"
+ )
+ self._appsink_audio.set_property("caps", caps)
+ self._appsink_audio.set_property("drop", True) # avoid overflow
+ self._appsink_audio.set_property("max-buffers", 500)
+ self._pipeline_record.add(self._appsink_audio)
+
+ self.camera_specs = cast(CameraSpecs, ReachyMiniCamSpecs)
+ self.set_resolution(RPICameraResolution.R1280x720)
+
+ self._appsink_video = Gst.ElementFactory.make("appsink")
+ caps_video = Gst.Caps.from_string("video/x-raw,format=BGR")
+ self._appsink_video.set_property("caps", caps_video)
+ self._appsink_video.set_property("drop", True) # avoid overflow
+ self._appsink_video.set_property("max-buffers", 1) # keep last image only
+ self._pipeline_record.add(self._appsink_video)
+
+ webrtcsrc = self._configure_webrtcsrc(signaling_host, signaling_port, peer_id)
+ self._pipeline_record.add(webrtcsrc)
+
+ self._pipeline_playback = Gst.Pipeline.new("audio_player")
+ self._init_pipeline_playback(
+ self._pipeline_playback, signaling_host, signaling_port
+ )
+ self._bus_playback = self._pipeline_playback.get_bus()
+ self._bus_playback.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+
+ def __del__(self) -> None:
+ """Destructor to ensure gstreamer resources are released."""
+ super().__del__()
+ self._loop.quit()
+ self._bus_record.remove_watch()
+ self._bus_playback.remove_watch()
+
+ def set_resolution(self, resolution: CameraResolution) -> None:
+ """Set the camera resolution."""
+ super().set_resolution(resolution)
+ self._resolution = resolution
+
+ def _configure_webrtcsrc(
+ self, signaling_host: str, signaling_port: int, peer_id: str
+ ) -> Gst.Element:
+ source = Gst.ElementFactory.make("webrtcsrc")
+ if not source:
+ raise RuntimeError(
+ "Failed to create webrtcsrc element. Is the GStreamer webrtc rust plugin installed?"
+ )
+
+ source.connect("pad-added", self._webrtcsrc_pad_added_cb)
+ signaller = source.get_property("signaller")
+ signaller.set_property("producer-peer-id", peer_id)
+ signaller.set_property("uri", f"ws://{signaling_host}:{signaling_port}")
+ return source
+
+ def _configure_webrtcbin(self, webrtcsrc: Gst.Element) -> None:
+ if isinstance(webrtcsrc, Gst.Bin):
+ webrtcbin_name = "webrtcbin0"
+ webrtcbin = webrtcsrc.get_by_name(webrtcbin_name)
+ assert webrtcbin is not None
+ # jitterbuffer has a default 200 ms buffer. Should be ok to lower this in localnetwork config
+ webrtcbin.set_property("latency", 100)
+
+ def _webrtcsrc_pad_added_cb(self, webrtcsrc: Gst.Element, pad: Gst.Pad) -> None:
+ self._configure_webrtcbin(webrtcsrc)
+ if pad.get_name().startswith("video"):
+ queue = Gst.ElementFactory.make("queue")
+
+ videoconvert = Gst.ElementFactory.make("videoconvert")
+ videoscale = Gst.ElementFactory.make("videoscale")
+ videorate = Gst.ElementFactory.make("videorate")
+
+ self._pipeline_record.add(queue)
+ self._pipeline_record.add(videoconvert)
+ self._pipeline_record.add(videoscale)
+ self._pipeline_record.add(videorate)
+ pad.link(queue.get_static_pad("sink"))
+
+ queue.link(videoconvert)
+ videoconvert.link(videoscale)
+ videoscale.link(videorate)
+ videorate.link(self._appsink_video)
+
+ queue.sync_state_with_parent()
+ videoconvert.sync_state_with_parent()
+ videoscale.sync_state_with_parent()
+ videorate.sync_state_with_parent()
+ self._appsink_video.sync_state_with_parent()
+
+ elif pad.get_name().startswith("audio"):
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+ self._pipeline_record.add(audioconvert)
+ self._pipeline_record.add(audioresample)
+
+ pad.link(audioconvert.get_static_pad("sink"))
+ audioconvert.link(audioresample)
+ audioresample.link(self._appsink_audio)
+
+ self._appsink_audio.sync_state_with_parent()
+ audioconvert.sync_state_with_parent()
+ audioresample.sync_state_with_parent()
+
+ def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
+ t = msg.type
+ if t == Gst.MessageType.EOS:
+ self.logger.warning("End-of-stream")
+ return False
+
+ elif t == Gst.MessageType.ERROR:
+ err, debug = msg.parse_error()
+ self.logger.error(f"Error: {err} {debug}")
+ return False
+
+ return True
+
+ def open(self) -> None:
+ """Open the video stream."""
+ self._pipeline_record.set_state(Gst.State.PLAYING)
+
+ def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
+ sample = appsink.try_pull_sample(20_000_000)
+ if sample is None:
+ return None
+ data = None
+ if isinstance(sample, Gst.Sample):
+ buf = sample.get_buffer()
+ if buf is None:
+ self.logger.warning("Buffer is None")
+
+ data = buf.extract_dup(0, buf.get_size())
+ return data
+
+ def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
+ """Read a sample from the audio card. Returns the sample or None if error.
+
+ Returns:
+ Optional[npt.NDArray[np.float32]]: The captured sample in raw format, or None if error.
+
+ """
+ sample = self._get_sample(self._appsink_audio)
+ if sample is None:
+ return None
+ return np.frombuffer(sample, dtype=np.float32).reshape(-1, 2)
+
+ def read(self) -> Optional[npt.NDArray[np.uint8]]:
+ """Read a frame from the camera. Returns the frame or None if error.
+
+ Returns:
+ Optional[npt.NDArray[np.uint8]]: The captured frame in BGR format, or None if error.
+
+ """
+ data = self._get_sample(self._appsink_video)
+ if data is None:
+ return None
+
+ arr = np.frombuffer(data, dtype=np.uint8).reshape(
+ (self.resolution[1], self.resolution[0], 3)
+ )
+ return arr
+
+ def close(self) -> None:
+ """Stop the pipeline."""
+ # self._loop.quit()
+ self._pipeline_record.set_state(Gst.State.NULL)
+
+ def start_recording(self) -> None:
+ """Open the audio card using GStreamer."""
+ pass # already started in open()
+
+ def stop_recording(self) -> None:
+ """Release the camera resource."""
+ pass # managed in close()
+
+ def _init_pipeline_playback(
+ self, pipeline: Gst.Pipeline, signaling_host: str, signaling_port: int
+ ) -> None:
+ """Initialize the audio playback pipeline."""
+ self._appsrc = Gst.ElementFactory.make("appsrc")
+ self._appsrc.set_property("format", Gst.Format.TIME)
+ self._appsrc.set_property("is-live", True)
+ caps = Gst.Caps.from_string(
+ f"audio/x-raw,format=F32LE,channels=1,rate={self.SAMPLE_RATE},layout=interleaved"
+ )
+ self._appsrc.set_property("caps", caps)
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+ opusenc = Gst.ElementFactory.make("opusenc")
+ queue = Gst.ElementFactory.make("queue")
+ rtpopuspay = Gst.ElementFactory.make("rtpopuspay")
+ udpsink = Gst.ElementFactory.make("udpsink")
+
+ udpsink.set_property("host", signaling_host)
+ udpsink.set_property("port", 5000)
+
+ pipeline.add(self._appsrc)
+ pipeline.add(audioconvert)
+ pipeline.add(audioresample)
+ pipeline.add(opusenc)
+ pipeline.add(queue)
+ pipeline.add(rtpopuspay)
+ pipeline.add(udpsink)
+
+ self._appsrc.link(audioconvert)
+ audioconvert.link(audioresample)
+ audioresample.link(opusenc)
+ opusenc.link(queue)
+ queue.link(rtpopuspay)
+ rtpopuspay.link(udpsink)
+
+ def start_playing(self) -> None:
+ """Open the audio output using GStreamer."""
+ self._pipeline_playback.set_state(Gst.State.PLAYING)
+
+ def stop_playing(self) -> None:
+ """Stop playing audio and release resources."""
+ self._pipeline_playback.set_state(Gst.State.NULL)
+
+ def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
+ """Push audio data to the output device."""
+ if self._appsrc is not None:
+ buf = Gst.Buffer.new_wrapped(data.tobytes())
+ self._appsrc.push_buffer(buf)
+
+ else:
+ self.logger.warning(
+ "AppSrc is not initialized. Call start_playing() first."
+ )
+
+ def play_sound(self, sound_file: str) -> None:
+ """Play a sound file.
+
+ Args:
+ sound_file (str): Path to the sound file to play.
+
+ """
+ self.logger.warning("Audio playback not implemented in WebRTC client.")
diff --git a/reachy_mini/src/reachy_mini/media/webrtc_daemon.py b/reachy_mini/src/reachy_mini/media/webrtc_daemon.py
new file mode 100644
index 0000000000000000000000000000000000000000..f718a9bd494f21348e88a61a043525e9a1589bc4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/media/webrtc_daemon.py
@@ -0,0 +1,266 @@
+"""WebRTC daemon.
+
+Starts a gstreamer webrtc pipeline to stream video and audio.
+"""
+
+import logging
+from threading import Thread
+
+import gi
+
+from reachy_mini.media.audio_utils import get_respeaker_card_number
+from reachy_mini.media.camera_constants import RPICameraResolution
+
+gi.require_version("Gst", "1.0")
+gi.require_version("GstApp", "1.0")
+
+from gi.repository import GLib, Gst # noqa: E402
+
+
+class GstWebRTC:
+ """WebRTC pipeline using GStreamer."""
+
+ def __init__(
+ self,
+ log_level: str = "INFO",
+ resolution: RPICameraResolution = RPICameraResolution.R1280x720,
+ ) -> None:
+ """Initialize the GStreamer WebRTC pipeline."""
+ self._logger = logging.getLogger(__name__)
+ self._logger.setLevel(log_level)
+ Gst.init(None)
+ self._loop = GLib.MainLoop()
+ self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
+ self._thread_bus_calls.start()
+ self._resolution = resolution
+
+ self._id_audio_card = get_respeaker_card_number()
+
+ self._pipeline_sender = Gst.Pipeline.new("reachymini_webrtc_sender")
+ self._bus_sender = self._pipeline_sender.get_bus()
+ self._bus_sender.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+
+ webrtcsink = self._configure_webrtc(self._pipeline_sender)
+
+ self._configure_video(self._pipeline_sender, webrtcsink)
+ self._configure_audio(self._pipeline_sender, webrtcsink)
+
+ self._pipeline_receiver = Gst.Pipeline.new("reachymini_webrtc_receiver")
+ self._bus_receiver = self._pipeline_receiver.get_bus()
+ self._bus_receiver.add_watch(
+ GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
+ )
+ self._configure_receiver(self._pipeline_receiver)
+
+ def __del__(self) -> None:
+ """Destructor to ensure gstreamer resources are released."""
+ self._loop.quit()
+ self._bus_sender.remove_watch()
+ self._bus_receiver.remove_watch()
+
+ def _configure_webrtc(self, pipeline: Gst.Pipeline) -> Gst.Element:
+ self._logger.debug("Configuring WebRTC")
+ webrtcsink = Gst.ElementFactory.make("webrtcsink")
+ if not webrtcsink:
+ raise RuntimeError(
+ "Failed to create webrtcsink element. Is the GStreamer webrtc rust plugin installed?"
+ )
+
+ meta_structure = Gst.Structure.new_empty("meta")
+ meta_structure.set_value("name", "reachymini")
+ webrtcsink.set_property("meta", meta_structure)
+ webrtcsink.set_property("run-signalling-server", True)
+
+ pipeline.add(webrtcsink)
+
+ return webrtcsink
+
+ def _configure_receiver(self, pipeline: Gst.Pipeline) -> None:
+ udpsrc = Gst.ElementFactory.make("udpsrc")
+ udpsrc.set_property("port", 5000)
+ caps = Gst.Caps.from_string(
+ "application/x-rtp,media=audio,encoding-name=OPUS,payload=96"
+ )
+ capsfilter = Gst.ElementFactory.make("capsfilter")
+ capsfilter.set_property("caps", caps)
+ rtpjitterbuffer = Gst.ElementFactory.make("rtpjitterbuffer")
+ rtpjitterbuffer.set_property(
+ "latency", 200
+ ) # configure latency depending on network conditions
+ rtpopusdepay = Gst.ElementFactory.make("rtpopusdepay")
+ opusdec = Gst.ElementFactory.make("opusdec")
+ queue = Gst.ElementFactory.make("queue")
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+ alsasink = Gst.ElementFactory.make("alsasink")
+ alsasink.set_property("device", f"hw:{self._id_audio_card},0")
+ alsasink.set_property("sync", False)
+
+ pipeline.add(udpsrc)
+ pipeline.add(capsfilter)
+ pipeline.add(rtpjitterbuffer)
+ pipeline.add(rtpopusdepay)
+ pipeline.add(opusdec)
+ pipeline.add(queue)
+ pipeline.add(audioconvert)
+ pipeline.add(audioresample)
+ pipeline.add(alsasink)
+
+ udpsrc.link(capsfilter)
+ capsfilter.link(rtpjitterbuffer)
+ rtpjitterbuffer.link(rtpopusdepay)
+ rtpopusdepay.link(opusdec)
+ opusdec.link(queue)
+ queue.link(audioconvert)
+ audioconvert.link(audioresample)
+ audioresample.link(alsasink)
+
+ @property
+ def resolution(self) -> tuple[int, int]:
+ """Get the current camera resolution as a tuple (width, height)."""
+ return (self._resolution.value[0], self._resolution.value[1])
+
+ @property
+ def framerate(self) -> int:
+ """Get the current camera framerate."""
+ return self._resolution.value[2]
+
+ def _configure_video(self, pipeline: Gst.Pipeline, webrtcsink: Gst.Element) -> None:
+ self._logger.debug("Configuring video")
+ libcamerasrc = Gst.ElementFactory.make("libcamerasrc")
+
+ caps = Gst.Caps.from_string(
+ f"video/x-raw,width={self.resolution[0]},height={self.resolution[1]},framerate={self.framerate}/1,format=YUY2,colorimetry=bt709,interlace-mode=progressive"
+ )
+ capsfilter = Gst.ElementFactory.make("capsfilter")
+ capsfilter.set_property("caps", caps)
+ queue = Gst.ElementFactory.make("queue")
+ v4l2h264enc = Gst.ElementFactory.make("v4l2h264enc")
+ extra_controls_structure = Gst.Structure.new_empty("extra-controls")
+ extra_controls_structure.set_value("repeat_sequence_header", 1)
+ extra_controls_structure.set_value("video_bitrate", 5_000_000)
+ v4l2h264enc.set_property("extra-controls", extra_controls_structure)
+ caps_h264 = Gst.Caps.from_string(
+ "video/x-h264,stream-format=byte-stream,alignment=au,level=(string)4"
+ )
+ capsfilter_h264 = Gst.ElementFactory.make("capsfilter")
+ capsfilter_h264.set_property("caps", caps_h264)
+
+ if not all(
+ [
+ libcamerasrc,
+ capsfilter,
+ queue,
+ v4l2h264enc,
+ capsfilter_h264,
+ ]
+ ):
+ raise RuntimeError("Failed to create GStreamer video elements")
+
+ pipeline.add(libcamerasrc)
+ pipeline.add(capsfilter)
+ pipeline.add(queue)
+ pipeline.add(v4l2h264enc)
+ pipeline.add(capsfilter_h264)
+
+ libcamerasrc.link(capsfilter)
+ capsfilter.link(queue)
+ queue.link(v4l2h264enc)
+ v4l2h264enc.link(capsfilter_h264)
+ capsfilter_h264.link(webrtcsink)
+
+ def _configure_audio(self, pipeline: Gst.Pipeline, webrtcsink: Gst.Element) -> None:
+ self._logger.debug("Configuring audio")
+ alsasrc = Gst.ElementFactory.make("alsasrc")
+ alsasrc.set_property("device", f"hw:{self._id_audio_card},0")
+ queue = Gst.ElementFactory.make("queue")
+ audioconvert = Gst.ElementFactory.make("audioconvert")
+ audioresample = Gst.ElementFactory.make("audioresample")
+ opusenc = Gst.ElementFactory.make("opusenc")
+ opusenc.set_property("audio-type", "restricted-lowdelay")
+ caps = Gst.Caps.from_string("audio/x-opus,channels=1,rate=48000")
+ capsfilter = Gst.ElementFactory.make("capsfilter")
+ capsfilter.set_property("caps", caps)
+
+ if not all(
+ [
+ alsasrc,
+ queue,
+ audioconvert,
+ audioresample,
+ opusenc,
+ capsfilter,
+ ]
+ ):
+ raise RuntimeError("Failed to create GStreamer audio elements")
+
+ pipeline.add(alsasrc)
+ pipeline.add(queue)
+ pipeline.add(audioconvert)
+ pipeline.add(audioresample)
+ pipeline.add(opusenc)
+ pipeline.add(capsfilter)
+
+ alsasrc.link(queue)
+ queue.link(audioconvert)
+ audioconvert.link(audioresample)
+ audioresample.link(opusenc)
+ opusenc.link(capsfilter)
+ capsfilter.link(webrtcsink)
+
+ def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
+ t = msg.type
+ if t == Gst.MessageType.EOS:
+ self._logger.warning("End-of-stream")
+ return False
+
+ elif t == Gst.MessageType.ERROR:
+ err, debug = msg.parse_error()
+ self._logger.error(f"Error: {err} {debug}")
+ return False
+
+ else:
+ # self._logger.warning(f"Unhandled message type: {t}")
+ pass
+
+ return True
+
+ def start(self) -> None:
+ """Start the WebRTC pipeline."""
+ self._logger.debug("Starting WebRTC")
+ self._pipeline_sender.set_state(Gst.State.PLAYING)
+ self._pipeline_receiver.set_state(Gst.State.PLAYING)
+
+ def pause(self) -> None:
+ """Pause the WebRTC pipeline."""
+ self._logger.debug("Pausing WebRTC")
+ self._pipeline_sender.set_state(Gst.State.PAUSED)
+ self._pipeline_receiver.set_state(Gst.State.PAUSED)
+
+ def stop(self) -> None:
+ """Stop the WebRTC pipeline."""
+ self._logger.debug("Stopping WebRTC")
+
+ self._pipeline_sender.set_state(Gst.State.NULL)
+ self._pipeline_receiver.set_state(Gst.State.NULL)
+
+
+if __name__ == "__main__":
+ import time
+
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
+ )
+
+ webrtc = GstWebRTC(log_level="DEBUG")
+ webrtc.start()
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ logging.info("User interrupted")
+ finally:
+ webrtc.stop()
diff --git a/reachy_mini/src/reachy_mini/motion/__init__.py b/reachy_mini/src/reachy_mini/motion/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..79c18705f8b1f39b91c5e70d8a3e4996c42298c4
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/motion/__init__.py
@@ -0,0 +1,4 @@
+"""Motion module for Reachy Mini.
+
+This module contains both utilities to create and play moves, as well as utilities to download datasets of recorded moves.
+"""
diff --git a/reachy_mini/src/reachy_mini/motion/goto.py b/reachy_mini/src/reachy_mini/motion/goto.py
new file mode 100644
index 0000000000000000000000000000000000000000..ddb9e5cb721f940f5d40ef27fc430d3728f7499a
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/motion/goto.py
@@ -0,0 +1,71 @@
+"""A goto move to a target head pose and/or antennas position."""
+
+import numpy as np
+import numpy.typing as npt
+
+from reachy_mini.utils.interpolation import (
+ InterpolationTechnique,
+ linear_pose_interpolation,
+ time_trajectory,
+)
+
+from .move import Move
+
+
+class GotoMove(Move):
+ """A goto move to a target head pose and/or antennas position."""
+
+ def __init__(
+ self,
+ start_head_pose: npt.NDArray[np.float64],
+ target_head_pose: npt.NDArray[np.float64] | None,
+ start_antennas: npt.NDArray[np.float64],
+ target_antennas: npt.NDArray[np.float64] | None,
+ start_body_yaw: float,
+ target_body_yaw: float | None,
+ duration: float,
+ method: InterpolationTechnique,
+ ):
+ """Set up the goto move."""
+ self.start_head_pose = start_head_pose
+ self.target_head_pose = (
+ target_head_pose if target_head_pose is not None else start_head_pose
+ )
+ self.start_antennas = start_antennas
+ self.target_antennas = (
+ target_antennas if target_antennas is not None else start_antennas
+ )
+ self.start_body_yaw = start_body_yaw
+ self.target_body_yaw = (
+ target_body_yaw if target_body_yaw is not None else start_body_yaw
+ )
+
+ self._duration = duration
+ self.method = method
+
+ @property
+ def duration(self) -> float:
+ """Duration of the goto in seconds."""
+ return self._duration
+
+ def evaluate(
+ self, t: float
+ ) -> tuple[
+ npt.NDArray[np.float64] | None, npt.NDArray[np.float64] | None, float | None
+ ]:
+ """Evaluate the goto at time t."""
+ interp_time = time_trajectory(t / self.duration, method=self.method)
+
+ interp_head_pose = linear_pose_interpolation(
+ self.start_head_pose, self.target_head_pose, interp_time
+ )
+ interp_antennas_joint = (
+ self.start_antennas
+ + (self.target_antennas - self.start_antennas) * interp_time
+ )
+ interp_body_yaw_joint = (
+ self.start_body_yaw
+ + (self.target_body_yaw - self.start_body_yaw) * interp_time
+ )
+
+ return interp_head_pose, interp_antennas_joint, interp_body_yaw_joint
diff --git a/reachy_mini/src/reachy_mini/motion/move.py b/reachy_mini/src/reachy_mini/motion/move.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6481384afcd19b100a66d4173c358eb9046be33
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/motion/move.py
@@ -0,0 +1,36 @@
+"""Module for defining motion moves on the ReachyMini robot."""
+
+from abc import ABC, abstractmethod
+
+import numpy as np
+import numpy.typing as npt
+
+
+class Move(ABC):
+ """Abstract base class for defining a move on the ReachyMini robot."""
+
+ @property
+ @abstractmethod
+ def duration(self) -> float:
+ """Duration of the move in seconds."""
+ pass
+
+ @abstractmethod
+ def evaluate(
+ self,
+ t: float,
+ ) -> tuple[
+ npt.NDArray[np.float64] | None, npt.NDArray[np.float64] | None, float | None
+ ]:
+ """Evaluate the move at time t, typically called at a high-frequency (eg. 100Hz).
+
+ Arguments:
+ t: The time at which to evaluate the move (in seconds). It will always be between 0 and duration.
+
+ Returns:
+ head: The head position (4x4 homogeneous matrix).
+ antennas: The antennas positions (rad).
+ body_yaw: The body yaw angle (rad).
+
+ """
+ pass
diff --git a/reachy_mini/src/reachy_mini/motion/recorded_move.py b/reachy_mini/src/reachy_mini/motion/recorded_move.py
new file mode 100644
index 0000000000000000000000000000000000000000..483040477141a1fc79580eac3a57e9fd86fcb151
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/motion/recorded_move.py
@@ -0,0 +1,132 @@
+import bisect # noqa: D100
+import json
+from glob import glob
+from pathlib import Path
+from typing import Any, Dict, List
+
+import numpy as np
+import numpy.typing as npt
+from huggingface_hub import snapshot_download
+
+from reachy_mini.motion.move import Move
+from reachy_mini.utils.interpolation import linear_pose_interpolation
+
+
+def lerp(v0: float, v1: float, alpha: float) -> float:
+ """Linear interpolation between two values."""
+ return v0 + alpha * (v1 - v0)
+
+
+class RecordedMove(Move):
+ """Represent a recorded move."""
+
+ def __init__(self, move: Dict[str, Any]) -> None:
+ """Initialize RecordedMove."""
+ self.move = move
+
+ self.description: str = self.move["description"]
+ self.timestamps: List[float] = self.move["time"]
+ self.trajectory: List[Dict[str, List[List[float]] | List[float] | float]] = (
+ self.move["set_target_data"]
+ )
+
+ self.dt: float = (self.timestamps[-1] - self.timestamps[0]) / len(
+ self.timestamps
+ )
+
+ @property
+ def duration(self) -> float:
+ """Get the duration of the recorded move."""
+ return len(self.trajectory) * self.dt
+
+ def evaluate(
+ self, t: float
+ ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], float]:
+ """Evaluate the move at time t.
+
+ Returns:
+ head: The head position (4x4 homogeneous matrix).
+ antennas: The antennas positions (rad).
+ body_yaw: The body yaw angle (rad).
+
+ """
+ # Under is Remi's emotions code, adapted
+ if t >= self.timestamps[-1]:
+ raise Exception("Tried to evaluate recorded move beyond its duration.")
+
+ # Locate the right interval in the recorded time array.
+ # 'index' is the insertion point which gives us the next timestamp.
+ index = bisect.bisect_right(self.timestamps, t)
+ # print(f"index: {index}, expected index: {t / self.dt:.0f}")
+ idx_prev = index - 1 if index > 0 else 0
+ idx_next = index if index < len(self.timestamps) else idx_prev
+
+ t_prev = self.timestamps[idx_prev]
+ t_next = self.timestamps[idx_next]
+
+ # Avoid division by zero (if by any chance two timestamps are identical).
+ if t_next == t_prev:
+ alpha = 0.0
+ else:
+ alpha = (t - t_prev) / (t_next - t_prev)
+
+ head_prev = np.array(self.trajectory[idx_prev]["head"], dtype=np.float64)
+ head_next = np.array(self.trajectory[idx_next]["head"], dtype=np.float64)
+ antennas_prev: List[float] = self.trajectory[idx_prev]["antennas"] # type: ignore[assignment]
+ antennas_next: List[float] = self.trajectory[idx_next]["antennas"] # type: ignore[assignment]
+ body_yaw_prev: float = self.trajectory[idx_prev].get("body_yaw", 0.0) # type: ignore[assignment]
+ body_yaw_next: float = self.trajectory[idx_next].get("body_yaw", 0.0) # type: ignore[assignment]
+ # check_collision = self.trajectory[idx_prev].get("check_collision", False)
+
+ # Interpolate to infer a better position at the current time.
+ # Joint interpolations are easy:
+
+ antennas_joints = np.array(
+ [
+ lerp(pos_prev, pos_next, alpha)
+ for pos_prev, pos_next in zip(antennas_prev, antennas_next)
+ ],
+ dtype=np.float64,
+ )
+
+ body_yaw = lerp(body_yaw_prev, body_yaw_next, alpha)
+
+ # Head position interpolation is more complex:
+ head_pose = linear_pose_interpolation(head_prev, head_next, alpha)
+
+ return head_pose, antennas_joints, body_yaw
+
+
+class RecordedMoves:
+ """Load a library of recorded moves from a HuggingFace dataset."""
+
+ def __init__(self, hf_dataset_name: str):
+ """Initialize RecordedMoves."""
+ self.hf_dataset_name = hf_dataset_name
+ self.local_path = snapshot_download(self.hf_dataset_name, repo_type="dataset")
+ self.moves: Dict[str, Any] = {}
+
+ self.process()
+
+ def process(self) -> None:
+ """Populate recorded moves and sounds."""
+ move_paths_tmp = glob(f"{self.local_path}/*.json")
+ move_paths = [Path(move_path) for move_path in move_paths_tmp]
+ for move_path in move_paths:
+ move_name = move_path.stem
+
+ move = json.load(open(move_path, "r"))
+ self.moves[move_name] = move
+
+ def get(self, move_name: str) -> RecordedMove:
+ """Get a recorded move by name."""
+ if move_name not in self.moves:
+ raise ValueError(
+ f"Move {move_name} not found in recorded moves library {self.hf_dataset_name}"
+ )
+
+ return RecordedMove(self.moves[move_name])
+
+ def list_moves(self) -> List[str]:
+ """List all moves in the loaded library."""
+ return list(self.moves.keys())
diff --git a/reachy_mini/src/reachy_mini/reachy_mini.py b/reachy_mini/src/reachy_mini/reachy_mini.py
new file mode 100644
index 0000000000000000000000000000000000000000..90293e57f7f177d9b20af53b89db5c1b6dfd4c10
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/reachy_mini.py
@@ -0,0 +1,739 @@
+"""Reachy Mini class for controlling a simulated or real Reachy Mini robot.
+
+This class provides methods to control the head and antennas of the Reachy Mini robot,
+set their target positions, and perform various behaviors such as waking up and going to sleep.
+
+It also includes methods for multimedia interactions like playing sounds and looking at specific points in the image frame or world coordinates.
+"""
+
+import asyncio
+import json
+import logging
+import time
+from typing import Dict, List, Optional, Union
+
+import cv2
+import numpy as np
+import numpy.typing as npt
+from asgiref.sync import async_to_sync
+from scipy.spatial.transform import Rotation as R
+
+from reachy_mini.daemon.utils import daemon_check
+from reachy_mini.io.protocol import GotoTaskRequest
+from reachy_mini.io.zenoh_client import ZenohClient
+from reachy_mini.media.media_manager import MediaBackend, MediaManager
+from reachy_mini.motion.move import Move
+from reachy_mini.utils.interpolation import InterpolationTechnique, minimum_jerk
+
+# Behavior definitions
+INIT_HEAD_POSE = np.eye(4)
+
+SLEEP_HEAD_JOINT_POSITIONS = [
+ 0,
+ -0.9848156658225817,
+ 1.2624661884298831,
+ -0.24390294527381684,
+ 0.20555342557667577,
+ -1.2363885150358267,
+ 1.0032234352772091,
+]
+
+
+SLEEP_ANTENNAS_JOINT_POSITIONS = [-3.05, 3.05]
+SLEEP_HEAD_POSE = np.array(
+ [
+ [0.911, 0.004, 0.413, -0.021],
+ [-0.004, 1.0, -0.001, 0.001],
+ [-0.413, -0.001, 0.911, -0.044],
+ [0.0, 0.0, 0.0, 1.0],
+ ]
+)
+
+
+class ReachyMini:
+ """Reachy Mini class for controlling a simulated or real Reachy Mini robot.
+
+ Args:
+ localhost_only (bool): If True, will only connect to localhost daemons, defaults to True.
+ spawn_daemon (bool): If True, will spawn a daemon to control the robot, defaults to False.
+ use_sim (bool): If True and spawn_daemon is True, will spawn a simulated robot, defaults to True.
+
+ """
+
+ def __init__(
+ self,
+ localhost_only: bool = True,
+ spawn_daemon: bool = False,
+ use_sim: bool = False,
+ timeout: float = 5.0,
+ automatic_body_yaw: bool = True,
+ log_level: str = "INFO",
+ media_backend: str = "default",
+ ) -> None:
+ """Initialize the Reachy Mini robot.
+
+ Args:
+ localhost_only (bool): If True, will only connect to localhost daemons, defaults to True.
+ spawn_daemon (bool): If True, will spawn a daemon to control the robot, defaults to False.
+ use_sim (bool): If True and spawn_daemon is True, will spawn a simulated robot, defaults to True.
+ timeout (float): Timeout for the client connection, defaults to 5.0 seconds.
+ automatic_body_yaw (bool): If True, the body yaw will be used to compute the IK and FK. Default is False.
+ log_level (str): Logging level, defaults to "INFO".
+ media_backend (str): Media backend to use, either "default" (OpenCV) or "gstreamer", defaults to "default".
+
+ It will try to connect to the daemon, and if it fails, it will raise an exception.
+
+ """
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+ daemon_check(spawn_daemon, use_sim)
+ self.client = ZenohClient(localhost_only)
+ self.client.wait_for_connection(timeout=timeout)
+ self.set_automatic_body_yaw(automatic_body_yaw)
+ self._last_head_pose: Optional[npt.NDArray[np.float64]] = None
+ self.is_recording = False
+
+ self.T_head_cam = np.eye(4)
+ self.T_head_cam[:3, 3][:] = [0.0437, 0, 0.0512]
+ self.T_head_cam[:3, :3] = np.array(
+ [
+ [0, 0, 1],
+ [-1, 0, 0],
+ [0, -1, 0],
+ ]
+ )
+
+ self.media_manager = self._configure_mediamanager(media_backend, log_level)
+
+ def __del__(self) -> None:
+ """Destroy the Reachy Mini instance.
+
+ The client is disconnected explicitly to avoid a thread pending issue.
+
+ """
+ self.client.disconnect()
+
+ def __enter__(self) -> "ReachyMini":
+ """Context manager entry point for Reachy Mini."""
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback) -> None: # type: ignore [no-untyped-def]
+ """Context manager exit point for Reachy Mini."""
+ self.media_manager.close()
+ self.client.disconnect()
+
+ @property
+ def media(self) -> MediaManager:
+ """Expose the MediaManager instance used by ReachyMini."""
+ return self.media_manager
+
+ def _configure_mediamanager(
+ self, media_backend: str, log_level: str
+ ) -> MediaManager:
+ if (
+ self.client.get_status()["wireless_version"]
+ and media_backend != "gstreamer"
+ ):
+ self.logger.warning(
+ "Wireless version detected, media backend should be set to 'gstreamer'. Reverting to no_media"
+ )
+ media_backend = "no_media"
+
+ mbackend = MediaBackend.DEFAULT
+ match media_backend.lower():
+ case "gstreamer":
+ if self.client.get_status()["wireless_version"]:
+ mbackend = MediaBackend.WEBRTC
+ else:
+ mbackend = MediaBackend.GSTREAMER
+ case "default":
+ mbackend = MediaBackend.DEFAULT
+ case "no_media":
+ mbackend = MediaBackend.NO_MEDIA
+ case "default_no_video":
+ mbackend = MediaBackend.DEFAULT_NO_VIDEO
+ case _:
+ raise ValueError(
+ f"Invalid media_backend '{media_backend}'. Supported values are 'default', 'gstreamer', 'no_media', 'default_no_video', and 'webrtc'."
+ )
+
+ return MediaManager(
+ use_sim=self.client.get_status()["simulation_enabled"],
+ backend=mbackend,
+ log_level=log_level,
+ signalling_host=self.client.get_status()["wlan_ip"],
+ )
+
+ def set_target(
+ self,
+ head: Optional[npt.NDArray[np.float64]] = None, # 4x4 pose matrix
+ antennas: Optional[
+ Union[npt.NDArray[np.float64], List[float]]
+ ] = None, # [right_angle, left_angle] (in rads)
+ body_yaw: Optional[float] = None, # Body yaw angle in radians
+ ) -> None:
+ """Set the target pose of the head and/or the target position of the antennas.
+
+ Args:
+ head (Optional[np.ndarray]): 4x4 pose matrix representing the head pose.
+ antennas (Optional[Union[np.ndarray, List[float]]]): 1D array with two elements representing the angles of the antennas in radians.
+ body_yaw (Optional[float]): Body yaw angle in radians.
+
+ Raises:
+ ValueError: If neither head nor antennas are provided, or if the shape of head is not (4, 4), or if antennas is not a 1D array with two elements.
+
+ """
+ if head is None and antennas is None and body_yaw is None:
+ raise ValueError(
+ "At least one of head, antennas or body_yaw must be provided."
+ )
+
+ if head is not None and not head.shape == (4, 4):
+ raise ValueError(f"Head pose must be a 4x4 matrix, got shape {head.shape}.")
+
+ if antennas is not None and not len(antennas) == 2:
+ raise ValueError(
+ "Antennas must be a list or 1D np array with two elements."
+ )
+
+ if body_yaw is not None and not isinstance(body_yaw, (int, float)):
+ raise ValueError("body_yaw must be a float.")
+
+ if head is not None:
+ self.set_target_head_pose(head)
+
+ if antennas is not None:
+ self.set_target_antenna_joint_positions(list(antennas))
+ # self._set_joint_positions(
+ # antennas_joint_positions=list(antennas),
+ # )
+
+ if body_yaw is not None:
+ self.set_target_body_yaw(body_yaw)
+
+ self._last_head_pose = head
+
+ record: Dict[str, float | List[float] | List[List[float]]] = {
+ "time": time.time(),
+ "body_yaw": body_yaw if body_yaw is not None else 0.0,
+ }
+ if head is not None:
+ record["head"] = head.tolist()
+ if antennas is not None:
+ record["antennas"] = list(antennas)
+ if body_yaw is not None:
+ record["body_yaw"] = body_yaw
+ self._set_record_data(record)
+
+ def goto_target(
+ self,
+ head: Optional[npt.NDArray[np.float64]] = None, # 4x4 pose matrix
+ antennas: Optional[
+ Union[npt.NDArray[np.float64], List[float]]
+ ] = None, # [right_angle, left_angle] (in rads)
+ duration: float = 0.5, # Duration in seconds for the movement, default is 0.5 seconds.
+ method: InterpolationTechnique = InterpolationTechnique.MIN_JERK, # can be "linear", "minjerk", "ease" or "cartoon", default is "minjerk")
+ body_yaw: float | None = 0.0, # Body yaw angle in radians
+ ) -> None:
+ """Go to a target head pose and/or antennas position using task space interpolation, in "duration" seconds.
+
+ Args:
+ head (Optional[np.ndarray]): 4x4 pose matrix representing the target head pose.
+ antennas (Optional[Union[np.ndarray, List[float]]]): 1D array with two elements representing the angles of the antennas in radians.
+ duration (float): Duration of the movement in seconds.
+ method (InterpolationTechnique): Interpolation method to use ("linear", "minjerk", "ease", "cartoon"). Default is "minjerk".
+ body_yaw (float | None): Body yaw angle in radians. Use None to keep the current yaw.
+
+ Raises:
+ ValueError: If neither head nor antennas are provided, or if duration is not positive.
+
+ """
+ if head is None and antennas is None and body_yaw is None:
+ raise ValueError(
+ "At least one of head, antennas or body_yaw must be provided."
+ )
+
+ if duration <= 0.0:
+ raise ValueError(
+ "Duration must be positive and non-zero. Use set_target() for immediate position setting."
+ )
+
+ req = GotoTaskRequest(
+ head=np.array(head, dtype=np.float64).flatten().tolist()
+ if head is not None
+ else None,
+ antennas=np.array(antennas, dtype=np.float64).flatten().tolist()
+ if antennas is not None
+ else None,
+ duration=duration,
+ method=method,
+ body_yaw=body_yaw,
+ )
+
+ task_uid = self.client.send_task_request(req)
+ self.client.wait_for_task_completion(task_uid, timeout=duration + 1.0)
+
+ def wake_up(self) -> None:
+ """Wake up the robot - go to the initial head position and play the wake up emote and sound."""
+ self.goto_target(INIT_HEAD_POSE, antennas=[0.0, 0.0], duration=2)
+ time.sleep(0.1)
+
+ # Toudoum
+ self.media.play_sound("wake_up.wav")
+
+ # Roll 20° to the left
+ pose = INIT_HEAD_POSE.copy()
+ pose[:3, :3] = R.from_euler("xyz", [20, 0, 0], degrees=True).as_matrix()
+ self.goto_target(pose, duration=0.2)
+
+ # Go back to the initial position
+ self.goto_target(INIT_HEAD_POSE, duration=0.2)
+
+ def goto_sleep(self) -> None:
+ """Put the robot to sleep by moving the head and antennas to a predefined sleep position."""
+ # Check if we are too far from the initial position
+ # Move to the initial position if necessary
+ current_positions, _ = self.get_current_joint_positions()
+ # init_positions = self.head_kinematics.ik(INIT_HEAD_POSE)
+ # Todo : get init position from the daemon?
+ init_positions = [
+ 6.959852054044218e-07,
+ 0.5251518455536499,
+ -0.668710345667336,
+ 0.6067086443974802,
+ -0.606711497194891,
+ 0.6687148024583701,
+ -0.5251586523105128,
+ ]
+ dist = np.linalg.norm(np.array(current_positions) - np.array(init_positions))
+ if dist > 0.2:
+ self.goto_target(INIT_HEAD_POSE, antennas=[0.0, 0.0], duration=1)
+ time.sleep(0.2)
+
+ # Pfiou
+ self.media.play_sound("go_sleep.wav")
+
+ # # Move to the sleep position
+ self.goto_target(
+ SLEEP_HEAD_POSE, antennas=SLEEP_ANTENNAS_JOINT_POSITIONS, duration=2
+ )
+
+ self._last_head_pose = SLEEP_HEAD_POSE
+ time.sleep(2)
+
+ def look_at_image(
+ self, u: int, v: int, duration: float = 1.0, perform_movement: bool = True
+ ) -> npt.NDArray[np.float64]:
+ """Make the robot head look at a point defined by a pixel position (u,v).
+
+ # TODO image of reachy mini coordinate system
+
+ Args:
+ u (int): Horizontal coordinate in image frame.
+ v (int): Vertical coordinate in image frame.
+ duration (float): Duration of the movement in seconds. If 0, the head will snap to the position immediately.
+ perform_movement (bool): If True, perform the movement. If False, only calculate and return the pose.
+
+ Returns:
+ np.ndarray: The calculated head pose as a 4x4 matrix.
+
+ Raises:
+ ValueError: If duration is negative.
+
+ """
+ if self.media_manager.camera is None:
+ raise RuntimeError("Camera is not initialized.")
+
+ # TODO this is false for the raspicam for now
+ assert 0 < u < self.media_manager.camera.resolution[0], (
+ f"u must be in [0, {self.media_manager.camera.resolution[0]}], got {u}."
+ )
+ assert 0 < v < self.media_manager.camera.resolution[1], (
+ f"v must be in [0, {self.media_manager.camera.resolution[1]}], got {v}."
+ )
+
+ if duration < 0:
+ raise ValueError("Duration can't be negative.")
+
+ if self.media.camera is None or self.media.camera.camera_specs is None:
+ raise RuntimeError("Camera specs not set.")
+
+ points = np.array([[[u, v]]], dtype=np.float32)
+ x_n, y_n = cv2.undistortPoints(
+ points,
+ self.media.camera.K, # type: ignore
+ self.media.camera.D, # type: ignore
+ )[0, 0]
+
+ ray_cam = np.array([x_n, y_n, 1.0])
+ ray_cam /= np.linalg.norm(ray_cam)
+
+ T_world_head = self.get_current_head_pose()
+ T_world_cam = T_world_head @ self.T_head_cam
+
+ R_wc = T_world_cam[:3, :3]
+ t_wc = T_world_cam[:3, 3]
+
+ ray_world = R_wc @ ray_cam
+
+ P_world = t_wc + ray_world
+
+ return self.look_at_world(
+ x=P_world[0],
+ y=P_world[1],
+ z=P_world[2],
+ duration=duration,
+ perform_movement=perform_movement,
+ )
+
+ def look_at_world(
+ self,
+ x: float,
+ y: float,
+ z: float,
+ duration: float = 1.0,
+ perform_movement: bool = True,
+ ) -> npt.NDArray[np.float64]:
+ """Look at a specific point in 3D space in Reachy Mini's reference frame.
+
+ TODO include image of reachy mini coordinate system
+
+ Args:
+ x (float): X coordinate in meters.
+ y (float): Y coordinate in meters.
+ z (float): Z coordinate in meters.
+ duration (float): Duration of the movement in seconds. If 0, the head will snap to the position immediately.
+ perform_movement (bool): If True, perform the movement. If False, only calculate and return the pose.
+
+ Returns:
+ np.ndarray: The calculated head pose as a 4x4 matrix.
+
+ Raises:
+ ValueError: If duration is negative.
+
+ """
+ if duration < 0:
+ raise ValueError("Duration can't be negative.")
+
+ # Head is at the origin, so vector from head to target position is directly the target position
+ # TODO FIX : Actually, the head frame is not the origin frame wrt the kinematics. Close enough for now.
+ target_position = np.array([x, y, z])
+ target_vector = target_position / np.linalg.norm(
+ target_position
+ ) # normalize the vector
+
+ # head_pointing straight vector
+ straight_head_vector = np.array([1, 0, 0])
+
+ # Calculate the rotation needed to align the head with the target vector
+ v1 = straight_head_vector
+ v2 = target_vector
+ axis = np.cross(v1, v2)
+ axis_norm = np.linalg.norm(axis)
+ if axis_norm < 1e-8:
+ # Vectors are (almost) parallel
+ if np.dot(v1, v2) > 0:
+ rot_mat = np.eye(3)
+ else:
+ # Opposite direction: rotate 180° around any perpendicular axis
+ perp = np.array([0, 1, 0]) if abs(v1[0]) < 0.9 else np.array([0, 0, 1])
+ axis = np.cross(v1, perp)
+ axis /= np.linalg.norm(axis)
+ rot_mat = R.from_rotvec(np.pi * axis).as_matrix()
+ else:
+ axis = axis / axis_norm
+ angle = np.arccos(np.clip(np.dot(v1, v2), -1.0, 1.0))
+ rotation_vector = angle * axis
+ rot_mat = R.from_rotvec(rotation_vector).as_matrix()
+
+ target_head_pose = np.eye(4)
+ target_head_pose[:3, :3] = rot_mat
+
+ # If perform_movement is True, execute the movement
+ if perform_movement:
+ # If duration is specified, use the goto_target method to move smoothly
+ # Otherwise, set the position immediately
+ if duration > 0:
+ self.goto_target(target_head_pose, duration=duration)
+ else:
+ self.set_target(target_head_pose)
+
+ return target_head_pose
+
+ def _goto_joint_positions(
+ self,
+ head_joint_positions: Optional[
+ List[float]
+ ] = None, # [yaw, stewart_platform x 6] length 7
+ antennas_joint_positions: Optional[
+ List[float]
+ ] = None, # [right_angle, left_angle] length 2
+ duration: float = 0.5, # Duration in seconds for the movement
+ ) -> None:
+ """Go to a target head joint positions and/or antennas joint positions using joint space interpolation, in "duration" seconds.
+
+ [Internal] Go to a target head joint positions and/or antennas joint positions using joint space interpolation, in "duration" seconds.
+
+ Args:
+ head_joint_positions (Optional[List[float]]): List of head joint positions in radians (length 7).
+ antennas_joint_positions (Optional[List[float]]): List of antennas joint positions in radians (length 2).
+ duration (float): Duration of the movement in seconds. Default is 0.5 seconds.
+
+ Raises:
+ ValueError: If neither head_joint_positions nor antennas_joint_positions are provided, or if duration is not positive.
+
+ """
+ if duration <= 0.0:
+ raise ValueError(
+ "Duration must be positive and non-zero. Use set_target() for immediate position setting."
+ )
+
+ cur_head, cur_antennas = self.get_current_joint_positions()
+ current = cur_head + cur_antennas
+
+ target = []
+ if head_joint_positions is not None:
+ target.extend(head_joint_positions)
+ else:
+ target.extend(cur_head)
+ if antennas_joint_positions is not None:
+ target.extend(antennas_joint_positions)
+ else:
+ target.extend(cur_antennas)
+
+ traj = minimum_jerk(np.array(current), np.array(target), duration)
+
+ t0 = time.time()
+ while time.time() - t0 < duration:
+ t = time.time() - t0
+ angles = traj(t)
+
+ head_joint = angles[:7] # First 7 angles for the head
+ antennas_joint = angles[7:]
+
+ self._set_joint_positions(list(head_joint), list(antennas_joint))
+ time.sleep(0.01)
+
+ def get_current_joint_positions(self) -> tuple[list[float], list[float]]:
+ """Get the current joint positions of the head and antennas.
+
+ Get the current joint positions of the head and antennas (in rad)
+
+ Returns:
+ tuple: A tuple containing two lists:
+ - List of head joint positions (rad) (length 7).
+ - List of antennas joint positions (rad) (length 2).
+
+ """
+ return self.client.get_current_joints()
+
+ def get_present_antenna_joint_positions(self) -> list[float]:
+ """Get the present joint positions of the antennas.
+
+ Get the present joint positions of the antennas (in rad)
+
+ Returns:
+ list: A list of antennas joint positions (rad) (length 2).
+
+ """
+ return self.get_current_joint_positions()[1]
+
+ def get_current_head_pose(self) -> npt.NDArray[np.float64]:
+ """Get the current head pose as a 4x4 matrix.
+
+ Get the current head pose as a 4x4 matrix.
+
+ Returns:
+ np.ndarray: A 4x4 matrix representing the current head pose.
+
+ """
+ return self.client.get_current_head_pose()
+
+ def _set_joint_positions(
+ self,
+ head_joint_positions: list[float] | None = None,
+ antennas_joint_positions: list[float] | None = None,
+ ) -> None:
+ """Set the joint positions of the head and/or antennas.
+
+ [Internal] Set the joint positions of the head and/or antennas.
+
+ Args:
+ head_joint_positions (Optional[List[float]]): List of head joint positions in radians (length 7).
+ antennas_joint_positions (Optional[List[float]]): List of antennas joint positions in radians (length 2).
+ record (Optional[Dict]): If provided, the command will be logged with the given record data.
+
+ """
+ cmd = {}
+
+ if head_joint_positions is not None:
+ assert len(head_joint_positions) == 7, (
+ f"Head joint positions must have length 7, got {head_joint_positions}."
+ )
+ cmd["head_joint_positions"] = list(head_joint_positions)
+
+ if antennas_joint_positions is not None:
+ assert len(antennas_joint_positions) == 2, "Antennas must have length 2."
+ cmd["antennas_joint_positions"] = list(antennas_joint_positions)
+
+ if not cmd:
+ raise ValueError(
+ "At least one of head_joint_positions or antennas must be provided."
+ )
+
+ self.client.send_command(json.dumps(cmd))
+
+ def set_target_head_pose(self, pose: npt.NDArray[np.float64]) -> None:
+ """Set the head pose to a specific 4x4 matrix.
+
+ Args:
+ pose (np.ndarray): A 4x4 matrix representing the desired head pose.
+ body_yaw (float): The yaw angle of the body, used to adjust the head pose.
+
+ Raises:
+ ValueError: If the shape of the pose is not (4, 4).
+
+ """
+ cmd = {}
+
+ if pose is not None:
+ assert pose.shape == (4, 4), (
+ f"Head pose should be a 4x4 matrix, got {pose.shape}."
+ )
+ cmd["head_pose"] = pose.tolist()
+ else:
+ raise ValueError("Pose must be provided as a 4x4 matrix.")
+
+ self.client.send_command(json.dumps(cmd))
+
+ def set_target_antenna_joint_positions(self, antennas: List[float]) -> None:
+ """Set the target joint positions of the antennas."""
+ cmd = {"antennas_joint_positions": antennas}
+ self.client.send_command(json.dumps(cmd))
+
+ def set_target_body_yaw(self, body_yaw: float) -> None:
+ """Set the target body yaw.
+
+ Args:
+ body_yaw (float): The yaw angle of the body in radians.
+
+ """
+ cmd = {"body_yaw": body_yaw}
+ self.client.send_command(json.dumps(cmd))
+
+ def start_recording(self) -> None:
+ """Start recording data."""
+ self.client.send_command(json.dumps({"start_recording": True}))
+ self.is_recording = True
+
+ def stop_recording(
+ self,
+ ) -> Optional[List[Dict[str, float | List[float] | List[List[float]]]]]:
+ """Stop recording data and return the recorded data."""
+ self.client.send_command(json.dumps({"stop_recording": True}))
+ self.is_recording = False
+ if not self.client.wait_for_recorded_data(timeout=5):
+ raise RuntimeError("Daemon did not provide recorded data in time!")
+ recorded_data = self.client.get_recorded_data(wait=False)
+
+ return recorded_data
+
+ def _set_record_data(
+ self, record: Dict[str, float | List[float] | List[List[float]]]
+ ) -> None:
+ """Set the record data to be logged by the backend.
+
+ Args:
+ record (Dict): The record data to be logged.
+
+ """
+ if not isinstance(record, dict):
+ raise ValueError("Record must be a dictionary.")
+
+ # Send the record data to the backend
+ self.client.send_command(json.dumps({"set_target_record": record}))
+
+ def enable_motors(self, ids: List[str] | None = None) -> None:
+ """Enable the motors.
+
+ Args:
+ ids (List[str] | None): List of motor names to enable. If None, all motors will be enabled.
+
+ """
+ self._set_torque(True, ids=ids)
+
+ def disable_motors(self, ids: List[str] | None = None) -> None:
+ """Disable the motors.
+
+ Args:
+ ids (List[str] | None): List of motor names to disable. If None, all motors will be disabled.
+
+ """
+ self._set_torque(False, ids=ids)
+
+ def _set_torque(self, on: bool, ids: List[str] | None = None) -> None:
+ self.client.send_command(json.dumps({"torque": on, "ids": ids}))
+
+ def enable_gravity_compensation(self) -> None:
+ """Enable gravity compensation for the head motors."""
+ self.client.send_command(json.dumps({"gravity_compensation": True}))
+
+ def disable_gravity_compensation(self) -> None:
+ """Disable gravity compensation for the head motors."""
+ self.client.send_command(json.dumps({"gravity_compensation": False}))
+
+ def set_automatic_body_yaw(self, body_yaw: float) -> None:
+ """Set the automatic body yaw.
+
+ Args:
+ body_yaw (float): The yaw angle of the body in radians.
+
+ """
+ self.client.send_command(json.dumps({"automatic_body_yaw": body_yaw}))
+
+ async def async_play_move(
+ self,
+ move: Move,
+ play_frequency: float = 100.0,
+ initial_goto_duration: float = 0.0,
+ ) -> None:
+ """Asynchronously play a Move.
+
+ Args:
+ move (Move): The Move object to be played.
+ play_frequency (float): The frequency at which to evaluate the move (in Hz).
+ initial_goto_duration (float): Duration for the initial goto to the starting position of the move (in seconds). If 0, no initial goto is performed.
+
+ """
+ if initial_goto_duration > 0.0:
+ start_head_pose, start_antennas_positions, start_body_yaw = move.evaluate(
+ 0.0
+ )
+ self.goto_target(
+ head=start_head_pose,
+ antennas=start_antennas_positions,
+ duration=initial_goto_duration,
+ body_yaw=start_body_yaw,
+ )
+
+ sleep_period = 1.0 / play_frequency
+
+ t0 = time.time()
+ while time.time() - t0 < move.duration:
+ t = min(time.time() - t0, move.duration - 1e-2)
+
+ head, antennas, body_yaw = move.evaluate(t)
+ if head is not None:
+ self.set_target_head_pose(head)
+ if body_yaw is not None:
+ self.set_target_body_yaw(body_yaw)
+ if antennas is not None:
+ self.set_target_antenna_joint_positions(list(antennas))
+
+ elapsed = time.time() - t0 - t
+ if elapsed < sleep_period:
+ await asyncio.sleep(sleep_period - elapsed)
+ else:
+ await asyncio.sleep(0.001)
+
+ play_move = async_to_sync(async_play_move)
diff --git a/reachy_mini/src/reachy_mini/utils/__init__.py b/reachy_mini/src/reachy_mini/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1166e2b99f48c75aa932fa76d9eae93593634f13
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/__init__.py
@@ -0,0 +1,46 @@
+"""Utility functions for Reachy Mini.
+
+These functions provide various utilities such as creating head poses, performing minimum jerk interpolation,
+checking if the Reachy Mini daemon is running, and performing linear pose interpolation.
+
+"""
+
+import numpy as np
+import numpy.typing as npt
+from scipy.spatial.transform import Rotation as R
+
+
+def create_head_pose(
+ x: float = 0,
+ y: float = 0,
+ z: float = 0,
+ roll: float = 0,
+ pitch: float = 0,
+ yaw: float = 0,
+ mm: bool = False,
+ degrees: bool = True,
+) -> npt.NDArray[np.float64]:
+ """Create a homogeneous transformation matrix representing a pose in 6D space (position and orientation).
+
+ Args:
+ x (float): X coordinate of the position.
+ y (float): Y coordinate of the position.
+ z (float): Z coordinate of the position.
+ roll (float): Roll angle
+ pitch (float): Pitch angle
+ yaw (float): Yaw angle
+ mm (bool): If True, convert position from millimeters to meters.
+ degrees (bool): If True, interpret roll, pitch, and yaw as degrees; otherwise as radians.
+
+ Returns:
+ np.ndarray: A 4x4 homogeneous transformation matrix representing the pose.
+
+ """
+ pose = np.eye(4)
+ rot = R.from_euler("xyz", [roll, pitch, yaw], degrees=degrees).as_matrix()
+ pose[:3, :3] = rot
+ pose[:, 3] = [x, y, z, 1]
+ if mm:
+ pose[:3, 3] /= 1000
+
+ return pose
diff --git a/reachy_mini/src/reachy_mini/utils/constants.py b/reachy_mini/src/reachy_mini/utils/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce102daf8c50cbe908dce6fd110997deb8a90554
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/constants.py
@@ -0,0 +1,9 @@
+"""Utility constants for the reachy_mini package."""
+
+from importlib.resources import files
+
+import reachy_mini
+
+URDF_ROOT_PATH: str = str(files(reachy_mini).joinpath("descriptions/reachy_mini/urdf"))
+ASSETS_ROOT_PATH: str = str(files(reachy_mini).joinpath("assets/"))
+MODELS_ROOT_PATH: str = str(files(reachy_mini).joinpath("assets/models"))
diff --git a/reachy_mini/src/reachy_mini/utils/hardware_config/__init__.py b/reachy_mini/src/reachy_mini/utils/hardware_config/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..89a11e958559e3f72f4b4469884b00af9fe5d450
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/hardware_config/__init__.py
@@ -0,0 +1 @@
+"""Hardware configuration utilities."""
diff --git a/reachy_mini/src/reachy_mini/utils/hardware_config/parser.py b/reachy_mini/src/reachy_mini/utils/hardware_config/parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..e370c9ee12e1c87da10af3b5d5edd52015346127
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/hardware_config/parser.py
@@ -0,0 +1,63 @@
+"""Module to parse Reachy Mini hardware configuration from a YAML file."""
+
+from dataclasses import dataclass
+
+import yaml
+
+
+@dataclass
+class MotorConfig:
+ """Motor configuration."""
+
+ id: int
+ offset: int
+ angle_limit_min: int
+ angle_limit_max: int
+ return_delay_time: int
+ shutdown_error: int
+ pid: tuple[int, int, int] | None = None
+
+
+@dataclass
+class SerialConfig:
+ """Serial configuration."""
+
+ baudrate: int
+
+
+@dataclass
+class ReachyMiniConfig:
+ """Reachy Mini configuration."""
+
+ version: str
+ serial: SerialConfig
+ motors: dict[str, MotorConfig]
+
+
+def parse_yaml_config(filename: str) -> ReachyMiniConfig:
+ """Parse the YAML configuration file and return a ReachyMiniConfig."""
+ with open(filename, "r") as file:
+ conf = yaml.load(file, Loader=yaml.FullLoader)
+
+ version = conf["version"]
+
+ motor_ids = {}
+ for motor in conf["motors"]:
+ for name, params in motor.items():
+ motor_ids[name] = MotorConfig(
+ id=params["id"],
+ offset=params["offset"],
+ angle_limit_min=params["lower_limit"],
+ angle_limit_max=params["upper_limit"],
+ return_delay_time=params["return_delay_time"],
+ shutdown_error=params["shutdown_error"],
+ pid=params.get("pid"),
+ )
+
+ serial = SerialConfig(baudrate=conf["serial"]["baudrate"])
+
+ return ReachyMiniConfig(
+ version=version,
+ serial=serial,
+ motors=motor_ids,
+ )
diff --git a/reachy_mini/src/reachy_mini/utils/interpolation.py b/reachy_mini/src/reachy_mini/utils/interpolation.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c39c031e5ddb9c00d6c7fb0003ce3cf3bd41457
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/interpolation.py
@@ -0,0 +1,227 @@
+"""Interpolation utilities for Reachy Mini."""
+
+from enum import Enum
+from typing import Callable, Optional, Tuple
+
+import numpy as np
+import numpy.typing as npt
+from scipy.spatial.transform import Rotation as R
+
+InterpolationFunc = Callable[[float], npt.NDArray[np.float64]]
+
+
+def minimum_jerk(
+ starting_position: npt.NDArray[np.float64],
+ goal_position: npt.NDArray[np.float64],
+ duration: float,
+ starting_velocity: Optional[npt.NDArray[np.float64]] = None,
+ starting_acceleration: Optional[npt.NDArray[np.float64]] = None,
+ final_velocity: Optional[npt.NDArray[np.float64]] = None,
+ final_acceleration: Optional[npt.NDArray[np.float64]] = None,
+) -> InterpolationFunc:
+ """Compute the mimimum jerk interpolation function from starting position to goal position."""
+ if starting_velocity is None:
+ starting_velocity = np.zeros(starting_position.shape)
+ if starting_acceleration is None:
+ starting_acceleration = np.zeros(starting_position.shape)
+ if final_velocity is None:
+ final_velocity = np.zeros(goal_position.shape)
+ if final_acceleration is None:
+ final_acceleration = np.zeros(goal_position.shape)
+
+ a0 = starting_position
+ a1 = starting_velocity
+ a2 = starting_acceleration / 2
+
+ d1, d2, d3, d4, d5 = [duration**i for i in range(1, 6)]
+
+ A = np.array(((d3, d4, d5), (3 * d2, 4 * d3, 5 * d4), (6 * d1, 12 * d2, 20 * d3)))
+ B = np.array(
+ (
+ goal_position - a0 - (a1 * d1) - (a2 * d2),
+ final_velocity - a1 - (2 * a2 * d1),
+ final_acceleration - (2 * a2),
+ )
+ )
+ X = np.linalg.solve(A, B)
+
+ coeffs = [a0, a1, a2, X[0], X[1], X[2]]
+
+ def f(t: float) -> npt.NDArray[np.float64]:
+ if t > duration:
+ return goal_position
+ return np.sum([c * t**i for i, c in enumerate(coeffs)], axis=0) # type: ignore[no-any-return]
+
+ return f
+
+
+def linear_pose_interpolation(
+ start_pose: npt.NDArray[np.float64], target_pose: npt.NDArray[np.float64], t: float
+) -> npt.NDArray[np.float64]:
+ """Linearly interpolate between two poses in 6D space."""
+ # Extract rotations
+ rot_start = R.from_matrix(start_pose[:3, :3])
+ rot_end = R.from_matrix(target_pose[:3, :3])
+
+ # Compute relative rotation q_rel such that rot_start * q_rel = rot_end
+ q_rel = rot_start.inv() * rot_end
+ # Convert to rotation vector (axis-angle)
+ rotvec_rel = q_rel.as_rotvec()
+ # Scale the rotation vector by t (allows t<0 or >1 for overshoot)
+ rot_interp = (rot_start * R.from_rotvec(rotvec_rel * t)).as_matrix()
+
+ # Extract translations
+ pos_start = start_pose[:3, 3]
+ pos_end = target_pose[:3, 3]
+ # Linear interpolation/extrapolation on translation
+ pos_interp = pos_start + (pos_end - pos_start) * t
+
+ # Compose homogeneous transformation
+ interp_pose = np.eye(4)
+ interp_pose[:3, :3] = rot_interp
+ interp_pose[:3, 3] = pos_interp
+
+ return interp_pose
+
+
+class InterpolationTechnique(str, Enum):
+ """Enumeration of interpolation techniques."""
+
+ LINEAR = "linear"
+ MIN_JERK = "minjerk"
+ EASE_IN_OUT = "ease_in_out"
+ CARTOON = "cartoon"
+
+
+def time_trajectory(
+ t: float, method: InterpolationTechnique = InterpolationTechnique.MIN_JERK
+) -> float:
+ """Compute the time trajectory value based on the specified interpolation method."""
+ if t < 0 or t > 1:
+ raise ValueError("time value is out of range [0,1]")
+
+ if method == InterpolationTechnique.LINEAR:
+ return t
+
+ elif method == InterpolationTechnique.MIN_JERK:
+ return 10 * t**3 - 15 * t**4 + 6 * t**5
+
+ elif method == InterpolationTechnique.EASE_IN_OUT:
+ if t < 0.5:
+ return 2 * t * t
+ else:
+ return 1 - ((-2 * t + 2) ** 2) / 2
+
+ elif method == InterpolationTechnique.CARTOON:
+ c1 = 1.70158
+ c2 = c1 * 1.525
+
+ if t < 0.5:
+ # phase in
+ return ((2 * t) ** 2 * ((c2 + 1) * 2 * t - c2)) / 2
+ else:
+ # phase out
+ return (((2 * t - 2) ** 2 * ((c2 + 1) * (2 * t - 2) + c2)) + 2) / 2
+
+ else:
+ raise ValueError(
+ "Unknown interpolation method: {} (possible values: {})".format(
+ method,
+ list(InterpolationTechnique),
+ )
+ )
+
+
+def delta_angle_between_mat_rot(
+ P: npt.NDArray[np.float64], Q: npt.NDArray[np.float64]
+) -> float:
+ """Compute the angle (in radians) between two 3x3 rotation matrices `P` and `Q`.
+
+ This is equivalent to the angular distance in axis-angle space.
+ It is computed via the trace of the relative rotation matrix.
+
+ References:
+ - https://math.stackexchange.com/questions/2113634/comparing-two-rotation-matrices
+ - http://www.boris-belousov.net/2016/12/01/quat-dist/
+
+ Args:
+ P: A 3x3 rotation matrix.
+ Q: Another 3x3 rotation matrix.
+
+ Returns:
+ The angle in radians between the two rotations.
+
+ """
+ R = np.dot(P, Q.T)
+ tr = (np.trace(R) - 1) / 2
+ tr = np.clip(tr, -1.0, 1.0) # Ensure numerical stability
+ return float(np.arccos(tr))
+
+
+def distance_between_poses(
+ pose1: npt.NDArray[np.float64], pose2: npt.NDArray[np.float64]
+) -> Tuple[float, float, float]:
+ """Compute three types of distance between two 4x4 homogeneous transformation matrices.
+
+ The result combines translation (in mm) and rotation (in degrees) using an arbitrary but
+ emotionally satisfying equivalence: 1 degree ≈ 1 mm.
+
+ Args:
+ pose1: A 4x4 homogeneous transformation matrix representing the first pose.
+ pose2: A 4x4 homogeneous transformation matrix representing the second pose.
+
+ Returns:
+ A tuple of:
+ - translation distance in meters,
+ - angular distance in radians,
+ - unhinged distance in magic-mm (translation in mm + rotation in degrees).
+
+ """
+ distance_translation = float(np.linalg.norm(pose1[:3, 3] - pose2[:3, 3]))
+ distance_angle = delta_angle_between_mat_rot(pose1[:3, :3], pose2[:3, :3])
+ unhinged_distance = distance_translation * 1000 + np.rad2deg(distance_angle)
+
+ return distance_translation, distance_angle, unhinged_distance
+
+
+def compose_world_offset(
+ T_abs: npt.NDArray[np.float64],
+ T_off_world: npt.NDArray[np.float64],
+ reorthonormalize: bool = False,
+) -> npt.NDArray[np.float64]:
+ """Compose an absolute world-frame pose with a world-frame offset.
+
+ - translations add in world: t_final = t_abs + t_off
+ - rotations compose in world: R_final = R_off @ R_abs
+ This rotates the frame in place (about its own origin) by a rotation
+ defined in world axes, and shifts it by a world translation.
+
+ Parameters
+ ----------
+ T_abs : (4,4) ndarray
+ Absolute pose in world frame.
+ T_off_world : (4,4) ndarray
+ Offset transform specified in world axes (dx,dy,dz in world; dR about world axes).
+ reorthonormalize : bool
+ If True, SVD-orthonormalize the resulting rotation to fight drift.
+
+ Returns
+ -------
+ T_final : (4,4) ndarray
+ Resulting pose in world frame.
+
+ """
+ R_abs, t_abs = T_abs[:3, :3], T_abs[:3, 3]
+ R_off, t_off = T_off_world[:3, :3], T_off_world[:3, 3]
+
+ R_final = R_off @ R_abs
+ if reorthonormalize:
+ U, _, Vt = np.linalg.svd(R_final)
+ R_final = U @ Vt
+
+ t_final = t_abs + t_off
+
+ T_final = np.eye(4)
+ T_final[:3, :3] = R_final
+ T_final[:3, 3] = t_final
+ return T_final
diff --git a/reachy_mini/src/reachy_mini/utils/parse_urdf_for_kinematics.py b/reachy_mini/src/reachy_mini/utils/parse_urdf_for_kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a38883f7871c316be611af2b79d45e90a4fb82c
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/parse_urdf_for_kinematics.py
@@ -0,0 +1,110 @@
+"""Generate kinematics data from URDF using Placo as preprocessing.
+
+The analytical kinematics need information from the URDF. This files computes the information and writes it in a .json file.
+"""
+
+import json
+from importlib.resources import files
+from typing import Any, Dict
+
+import numpy as np # noqa: D100
+from placo_utils.tf import tf
+
+import reachy_mini
+from reachy_mini.kinematics.placo_kinematics import PlacoKinematics
+
+
+def get_data() -> Dict[str, Any]:
+ """Generate the urdf_kinematics.json file."""
+ urdf_root_path: str = str(
+ files(reachy_mini).joinpath("descriptions/reachy_mini/urdf")
+ )
+
+ placo_kinematics = PlacoKinematics(urdf_root_path, 0.02)
+ robot = placo_kinematics.robot
+
+ placo_kinematics.fk(np.array([0.0] * 7), no_iterations=20)
+ robot.update_kinematics()
+
+ # Measuring lengths for the arm and branch (constants could be used)
+ T_world_head_home = robot.get_T_world_frame("head").copy()
+ T_world_1 = robot.get_T_world_frame("stewart_1")
+ T_world_arm1 = robot.get_T_world_frame("passive_1_link_x")
+ T_1_arm1 = np.linalg.inv(T_world_1) @ T_world_arm1
+ arm_z = T_1_arm1[2, 3]
+ motor_arm_length = np.linalg.norm(T_1_arm1[:2, 3])
+
+ T_world_branch1 = robot.get_T_world_frame("closing_1_2")
+ T_arm1_branch1 = np.linalg.inv(T_world_arm1) @ T_world_branch1
+ rod_length = np.linalg.norm(T_arm1_branch1[:3, 3])
+
+ motors = [
+ {
+ "name": "stewart_1",
+ "branch_frame": "closing_1_2",
+ "offset": 0,
+ "solution": 0,
+ },
+ {
+ "name": "stewart_2",
+ "branch_frame": "closing_2_2",
+ "offset": 0,
+ "solution": 1,
+ },
+ {
+ "name": "stewart_3",
+ "branch_frame": "closing_3_2",
+ "offset": 0,
+ "solution": 0,
+ },
+ {
+ "name": "stewart_4",
+ "branch_frame": "closing_4_2",
+ "offset": 0,
+ "solution": 1,
+ },
+ {
+ "name": "stewart_5",
+ "branch_frame": "closing_5_2",
+ "offset": 0,
+ "solution": 0,
+ },
+ {
+ "name": "stewart_6",
+ "branch_frame": "passive_7_link_y",
+ "offset": 0,
+ "solution": 1,
+ },
+ ]
+
+ for motor in motors:
+ T_world_branch = robot.get_T_world_frame(motor["branch_frame"])
+ T_head_branch = np.linalg.inv(T_world_head_home) @ T_world_branch
+ T_world_motor = robot.get_T_world_frame(motor["name"]) @ tf.translation_matrix(
+ (0, 0, arm_z)
+ )
+ motor["T_motor_world"] = np.linalg.inv(T_world_motor).tolist()
+ motor["branch_position"] = T_head_branch[:3, 3].tolist()
+ motor["limits"] = robot.get_joint_limits(motor["name"]).tolist()
+
+ data = {
+ "motor_arm_length": motor_arm_length,
+ "rod_length": rod_length,
+ "head_z_offset": placo_kinematics.head_z_offset,
+ "motors": motors,
+ }
+
+ return data
+
+
+def main() -> None:
+ """Generate the urdf_kinematics.json file."""
+ assets_root_path: str = str(files(reachy_mini).joinpath("assets/"))
+ data = get_data()
+ print(assets_root_path + "/" + "kinematics_data.json")
+ with open(assets_root_path + "/" + "kinematics_data.json", "w") as f:
+ json.dump(data, f, indent=4)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/src/reachy_mini/utils/rerun.py b/reachy_mini/src/reachy_mini/utils/rerun.py
new file mode 100644
index 0000000000000000000000000000000000000000..eeab8e7ba3c21d63095deb883630ec7f337839ae
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/rerun.py
@@ -0,0 +1,320 @@
+"""Rerun logging for Reachy Mini.
+
+This module provides functionality to log the state of the Reachy Mini robot to Rerun,
+ a tool for visualizing and debugging robotic systems.
+
+It includes methods to log joint positions, camera images, and other relevant data.
+"""
+
+import json
+import logging
+import os
+import tempfile
+import time
+from threading import Event, Thread
+from typing import Dict, List, Optional
+
+import numpy as np
+import requests
+import rerun as rr
+from scipy.spatial.transform import Rotation as R
+from urdf_parser_py import urdf
+
+from reachy_mini.kinematics.placo_kinematics import PlacoKinematics
+from reachy_mini.media.media_manager import MediaBackend
+from reachy_mini.reachy_mini import ReachyMini
+
+
+class Rerun:
+ """Rerun logging for Reachy Mini."""
+
+ def __init__(
+ self,
+ reachymini: ReachyMini,
+ app_id: str = "reachy_mini_rerun",
+ spawn: bool = True,
+ ):
+ """Initialize the Rerun logging for Reachy Mini.
+
+ Args:
+ reachymini (ReachyMini): The Reachy Mini instance to log.
+ app_id (str): The application ID for Rerun. Defaults to reachy_mini_daemon.
+ spawn (bool): If True, spawn the Rerun server. Defaults to True.
+
+ """
+ rr.init(app_id, spawn=spawn)
+ self.app_id = app_id
+ self._reachymini = reachymini
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(reachymini.logger.getEffectiveLevel())
+
+ self._robot_ip = "localhost"
+ if self._reachymini.client.get_status()["wireless_version"]:
+ self._robot_ip = self._reachymini.client.get_status()["wlan_ip"]
+
+ self.recording = rr.get_global_data_recording()
+
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+
+ urdf_path = os.path.join(
+ script_dir, "../descriptions/reachy_mini/urdf/robot_no_collision.urdf"
+ )
+ asset_path = os.path.join(script_dir, "../descriptions/reachy_mini/urdf")
+
+ fixed_urdf = self.set_absolute_path_to_urdf(urdf_path, asset_path)
+ self.logger.debug(
+ f"Using URDF file: {fixed_urdf} with absolute paths for Rerun."
+ )
+
+ self.head_kinematics = PlacoKinematics(fixed_urdf)
+
+ self.urdf_model = urdf.URDF.from_xml_file(fixed_urdf)
+ self._joints_by_name: Dict[str, urdf.Joint] = {
+ joint.name: joint for joint in self.urdf_model.joints
+ }
+ self._entity_paths = UrdfEntityPaths(self.urdf_model, "ReachyMini")
+
+ rr.set_time("reachymini", timestamp=time.time(), recording=self.recording)
+
+ # Use the native URDF loader in Rerun to visualize Reachy Mini's model
+ # Logging as non-static to allow updating joint positions
+ rr.log_file_from_path(
+ fixed_urdf,
+ static=False,
+ entity_path_prefix=self._entity_paths.prefix,
+ recording=self.recording,
+ )
+
+ self.running = Event()
+ self.thread_log_camera: Optional[Thread] = None
+ if (
+ reachymini.media.backend == MediaBackend.GSTREAMER
+ or reachymini.media.backend == MediaBackend.DEFAULT
+ ):
+ self.thread_log_camera = Thread(target=self.log_camera, daemon=True)
+ self.thread_log_movements = Thread(target=self.log_movements, daemon=True)
+
+ def set_absolute_path_to_urdf(self, urdf_path: str, abs_path: str) -> str:
+ """Set the absolute paths in the URDF file. Rerun cannot read the "package://" paths."""
+ with open(urdf_path, "r") as f:
+ urdf_content = f.read()
+ urdf_content_mod = urdf_content.replace("package://", f"file://{abs_path}/")
+
+ with tempfile.NamedTemporaryFile("w", delete=False, suffix=".urdf") as tmp_file:
+ tmp_file.write(urdf_content_mod)
+ return tmp_file.name
+
+ def start(self) -> None:
+ """Start the Rerun logging thread."""
+ if self.thread_log_camera is not None:
+ self.thread_log_camera.start()
+ self.thread_log_movements.start()
+
+ def stop(self) -> None:
+ """Stop the Rerun logging thread."""
+ self.running.set()
+
+ def _get_joint(self, joint_name: str) -> urdf.Joint:
+ try:
+ return self._joints_by_name[joint_name]
+ except KeyError as exc:
+ raise RuntimeError(f"Invalid joint name: {joint_name}") from exc
+
+ def _joint_entity_path(self, joint: urdf.Joint) -> str:
+ return self._entity_paths.joint_paths[joint.name]
+
+ def log_camera(self) -> None:
+ """Log the camera image to Rerun."""
+ if self._reachymini.media.camera is None:
+ self.logger.warning("Camera is not initialized.")
+ return
+
+ self.logger.info("Starting camera logging to Rerun.")
+
+ while not self.running.is_set():
+ rr.set_time("reachymini", timestamp=time.time(), recording=self.recording)
+ frame = self._reachymini.media.get_frame()
+ if frame is not None:
+ if isinstance(frame, bytes):
+ self.logger.warning(
+ "Received frame is jpeg. Please use default backend."
+ )
+ return
+
+ else:
+ return
+
+ K = np.array(
+ [
+ [550.3564, 0.0, 638.0112],
+ [0.0, 549.1653, 364.589],
+ [0.0, 0.0, 1.0],
+ ]
+ )
+
+ cam_joint = self._get_joint("camera_optical_frame")
+ cam_path = self._joint_entity_path(cam_joint)
+ rr.log(
+ f"{cam_path}/image",
+ rr.Pinhole(
+ image_from_camera=rr.datatypes.Mat3x3(K),
+ width=frame.shape[1],
+ height=frame.shape[0],
+ image_plane_distance=0.8,
+ camera_xyz=rr.ViewCoordinates.RDF,
+ ),
+ rr.Image(frame, color_model="bgr").compress(),
+ recording=self.recording,
+ )
+
+ time.sleep(0.03) # ~30fps
+
+ def _log_joint_angle(
+ self,
+ joint_name: str,
+ angle: float,
+ axis: list[float] | None = None,
+ ) -> None:
+ """Log the joint angle to Rerun."""
+ joint = self._get_joint(joint_name)
+ joint_path = self._joint_entity_path(joint)
+
+ base_euler = joint.origin.rotation or [0.0, 0.0, 0.0]
+
+ # if we specify an axis override, use it; otherwise, use the joint's defined axis
+ effective_axis = (
+ np.array(axis)
+ if axis is not None
+ else np.array(joint.axis or [1.0, 0.0, 0.0])
+ )
+
+ target_euler = np.array(base_euler) + (effective_axis * angle)
+ target_translation = joint.origin.xyz or [0.0, 0.0, 0.0]
+
+ rr.log(
+ joint_path,
+ rr.Transform3D(
+ translation=target_translation,
+ quaternion=R.from_euler("xyz", target_euler).as_quat(),
+ ),
+ recording=self.recording,
+ )
+
+ def log_movements(self) -> None:
+ """Log the movement data to Rerun."""
+ url = f"http://{self._robot_ip}:8000/api/state/full"
+
+ params = {
+ "with_control_mode": "false",
+ "with_head_pose": "false",
+ "with_target_head_pose": "false",
+ "with_head_joints": "true",
+ "with_target_head_joints": "false",
+ "with_body_yaw": "false", # already in head_joints
+ "with_target_body_yaw": "false",
+ "with_antenna_positions": "true",
+ "with_target_antenna_positions": "false",
+ "use_pose_matrix": "false",
+ "with_passive_joints": "true",
+ }
+
+ while not self.running.is_set():
+ msg = requests.get(url, params=params)
+
+ if msg.status_code != 200:
+ self.logger.error(
+ f"Request failed with status {msg.status_code}: {msg.text}"
+ )
+ time.sleep(0.1)
+ continue
+ try:
+ data = json.loads(msg.text)
+ self.logger.debug(f"Data: {data}")
+ except Exception:
+ continue
+
+ rr.set_time("reachymini", timestamp=time.time(), recording=self.recording)
+
+ if "antennas_position" in data and data["antennas_position"] is not None:
+ antennas = data["antennas_position"]
+ if antennas is not None:
+ self._log_joint_angle("left_antenna", antennas[0])
+ self._log_joint_angle("right_antenna", antennas[1])
+
+ if "head_joints" in data and data["head_joints"] is not None:
+ head_joints = data["head_joints"]
+
+ # The joint axis definitions in the URDF do not match the real axis of rotation
+ # due to URDF not supporting ball joints properly.
+ self._log_joint_angle("yaw_body", -head_joints[0], axis=[0, 0, 1])
+ self._log_joint_angle("stewart_1", -head_joints[1], axis=[0, 1, 0])
+ self._log_joint_angle("stewart_2", head_joints[2], axis=[0, 1, 0])
+ self._log_joint_angle("stewart_3", -head_joints[3], axis=[0, 1, 0])
+ self._log_joint_angle("stewart_4", head_joints[4], axis=[0, 1, 0])
+ self._log_joint_angle("stewart_5", -head_joints[5], axis=[0, 1, 0])
+ self._log_joint_angle("stewart_6", head_joints[6], axis=[0, 1, 0])
+
+ if "passive_joints" in data and data["passive_joints"] is not None:
+ passive_joints = data["passive_joints"]
+
+ for axis_idx, axis in enumerate(["x", "y", "z"]):
+ for i in range(1, 8):
+ joint_name = f"passive_{i}_{axis}"
+ value_index = (i - 1) * 3 + axis_idx
+
+ override_axis = [0.0, 0.0, 0.0]
+ override_axis[axis_idx] = 1.0
+ self._log_joint_angle(
+ joint_name,
+ passive_joints[value_index],
+ axis=override_axis,
+ )
+
+ time.sleep(0.1)
+
+
+class UrdfEntityPaths:
+ """Helper for constructing link/joint entity paths that match the native URDF logger."""
+
+ def __init__(self, model: urdf.URDF, entity_path_prefix: Optional[str]) -> None:
+ """Construct a new `UrdfEntityPaths` instance.
+
+ Args:
+ model (urdf.URDF): The URDF model.
+ entity_path_prefix (Optional[str]): The prefix for entity paths.
+
+ """
+ self._model = model
+ self.prefix = entity_path_prefix
+ self.link_paths: Dict[str, str] = {}
+ self.joint_paths: Dict[str, str] = {}
+
+ base_parts = [part for part in (self.prefix, model.name) if part]
+ self._base_path = "/".join(base_parts)
+
+ children_map: Dict[str, List[urdf.Joint]] = {}
+ for joint in model.joints:
+ children_map.setdefault(joint.parent, []).append(joint)
+
+ root_link = model.get_root()
+ self._build_paths(root_link, self._base_path, children_map)
+
+ def _build_paths(
+ self,
+ link_name: str,
+ parent_path: str,
+ children_map: Dict[str, List[urdf.Joint]],
+ ) -> None:
+ link_path = self._join(parent_path, link_name)
+ self.link_paths[link_name] = link_path
+
+ for joint in children_map.get(link_name, []):
+ joint_path = self._join(link_path, joint.name)
+ self.joint_paths[joint.name] = joint_path
+ self._build_paths(joint.child, joint_path, children_map)
+
+ @staticmethod
+ def _join(parent: str, child: str) -> str:
+ if parent:
+ return f"{parent}/{child}"
+ return child
diff --git a/reachy_mini/src/reachy_mini/utils/wireless_version/__init__.py b/reachy_mini/src/reachy_mini/utils/wireless_version/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8db2704a00479b0c46e41536d2b86a417177b991
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/wireless_version/__init__.py
@@ -0,0 +1 @@
+"""Utility functions for working with wireless version."""
diff --git a/reachy_mini/src/reachy_mini/utils/wireless_version/update.py b/reachy_mini/src/reachy_mini/utils/wireless_version/update.py
new file mode 100644
index 0000000000000000000000000000000000000000..b12367010c96c6d25b8aa3c128872481900b8e57
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/wireless_version/update.py
@@ -0,0 +1,20 @@
+"""Module to handle software updates for the Reachy Mini wireless."""
+
+import logging
+
+from .utils import call_logger_wrapper
+
+
+async def update_reachy_mini(pre_release: bool, logger: logging.Logger) -> None:
+ """Perform a software update by upgrading the reachy_mini package and restarting the daemon."""
+ extra_args = []
+ if pre_release:
+ extra_args.append("--pre")
+
+ await call_logger_wrapper(
+ ["pip", "install", "--upgrade", "reachy_mini[wireless-version]"] + extra_args,
+ logger,
+ )
+ await call_logger_wrapper(
+ ["sudo", "systemctl", "restart", "reachy-mini-daemon"], logger
+ )
diff --git a/reachy_mini/src/reachy_mini/utils/wireless_version/update_available.py b/reachy_mini/src/reachy_mini/utils/wireless_version/update_available.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8320b42bf8834df7e844a25dc59e4666867c409
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/wireless_version/update_available.py
@@ -0,0 +1,61 @@
+"""Check if an update is available for Reachy Mini Wireless.
+
+For now, this only checks if a new version of "reachy_mini" is available on PyPI.
+"""
+
+from importlib.metadata import version
+
+import requests
+import semver
+
+
+def is_update_available(package_name: str, pre_release: bool) -> bool:
+ """Check if an update is available for the given package."""
+ pypi_version = get_pypi_version(package_name, pre_release)
+ local_version = get_local_version(package_name)
+
+ is_update_available = pypi_version > local_version
+ assert isinstance(is_update_available, bool)
+
+ return is_update_available
+
+
+def get_pypi_version(package_name: str, pre_release: bool) -> semver.Version:
+ """Get the latest version of a package from PyPI."""
+ url = f"https://pypi.org/pypi/{package_name}/json"
+ response = requests.get(url)
+ response.raise_for_status()
+ data = response.json()
+
+ version = data["info"]["version"]
+
+ if pre_release:
+ releases = list(data["releases"].keys())
+ pre_version = _semver_version(releases[-1])
+ if pre_version > version:
+ return pre_version
+
+ return _semver_version(version)
+
+
+def get_local_version(package_name: str) -> semver.Version:
+ """Get the currently installed version of a package."""
+ return _semver_version(version(package_name))
+
+
+def _semver_version(v: str) -> semver.Version:
+ """Convert a version string to a semver.Version object, handling pypi pre-release formats."""
+ try:
+ return semver.Version.parse(v)
+ except ValueError:
+ version_parts = v.split(".")
+ if len(version_parts) < 3:
+ raise ValueError(f"Invalid version string: {v}")
+
+ patch_part = version_parts[2]
+ if "rc" in patch_part:
+ patch, rc = patch_part.split("rc", 1)
+ v_clean = f"{version_parts[0]}.{version_parts[1]}.{patch}-rc.{rc}"
+ return semver.Version.parse(v_clean)
+
+ raise ValueError(f"Invalid version string: {v}")
diff --git a/reachy_mini/src/reachy_mini/utils/wireless_version/utils.py b/reachy_mini/src/reachy_mini/utils/wireless_version/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..dff1b3650f38f259c1b35fc50cafc4f79572c809
--- /dev/null
+++ b/reachy_mini/src/reachy_mini/utils/wireless_version/utils.py
@@ -0,0 +1,39 @@
+"""Utility functions for running shell commands asynchronously with real-time logging."""
+
+import asyncio
+import logging
+from typing import Callable
+
+
+async def call_logger_wrapper(command: list[str], logger: logging.Logger) -> None:
+ """Run a command asynchronously, streaming stdout and stderr to logger in real time.
+
+ Args:
+ command: list or tuple of command arguments (not a string)
+ logger: logger object with .info and .error methods
+
+ """
+ process = await asyncio.create_subprocess_exec(
+ *command,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+
+ async def stream_output(
+ stream: asyncio.StreamReader,
+ log_func: Callable[[str], None],
+ ) -> None:
+ while True:
+ line = await stream.readline()
+ if not line:
+ break
+ log_func(line.decode().rstrip())
+
+ tasks = []
+ if process.stdout is not None:
+ tasks.append(asyncio.create_task(stream_output(process.stdout, logger.info)))
+ if process.stderr is not None:
+ tasks.append(asyncio.create_task(stream_output(process.stderr, logger.error)))
+
+ await asyncio.gather(*tasks)
+ await process.wait()
diff --git a/reachy_mini/tests/faulty_app/README.md b/reachy_mini/tests/faulty_app/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..63915a996d683aad5ba055ef43f9d2ca093c5b51
--- /dev/null
+++ b/reachy_mini/tests/faulty_app/README.md
@@ -0,0 +1 @@
+# faulty_app - A ReachyMini application
\ No newline at end of file
diff --git a/reachy_mini/tests/faulty_app/faulty_app/__init__.py b/reachy_mini/tests/faulty_app/faulty_app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/reachy_mini/tests/faulty_app/faulty_app/main.py b/reachy_mini/tests/faulty_app/faulty_app/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7834aefc7b3cdd3805128f66b4b5ddb96a93ddcc
--- /dev/null
+++ b/reachy_mini/tests/faulty_app/faulty_app/main.py
@@ -0,0 +1,9 @@
+import threading
+
+from reachy_mini import ReachyMini, ReachyMiniApp
+
+
+class FaultyApp(ReachyMiniApp):
+ def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
+ raise RuntimeError("This is a faulty app for testing purposes.")
+
diff --git a/reachy_mini/tests/faulty_app/pyproject.toml b/reachy_mini/tests/faulty_app/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..5fe572f708a23be63839c0853fdaf09b46e7196f
--- /dev/null
+++ b/reachy_mini/tests/faulty_app/pyproject.toml
@@ -0,0 +1,18 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+
+[project]
+name = "faulty_app"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.8"
+dependencies = [
+ "reachy-mini"
+]
+keywords = ["reachy-mini-app"]
+
+[project.entry-points."reachy_mini_apps"]
+faulty_app = "faulty_app.main:FaultyApp"
\ No newline at end of file
diff --git a/reachy_mini/tests/ok_app/README.md b/reachy_mini/tests/ok_app/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3297f129a114bac224eb602bce4300e26d01ad19
--- /dev/null
+++ b/reachy_mini/tests/ok_app/README.md
@@ -0,0 +1 @@
+# ok_app - A ReachyMini application
\ No newline at end of file
diff --git a/reachy_mini/tests/ok_app/ok_app/__init__.py b/reachy_mini/tests/ok_app/ok_app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/reachy_mini/tests/ok_app/ok_app/main.py b/reachy_mini/tests/ok_app/ok_app/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..80e245f3817b53dd35cb4cc06f0f0f2e08f6943f
--- /dev/null
+++ b/reachy_mini/tests/ok_app/ok_app/main.py
@@ -0,0 +1,10 @@
+import threading
+import time
+
+from reachy_mini import ReachyMini, ReachyMiniApp
+
+
+class OkApp(ReachyMiniApp):
+ def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
+ while not stop_event.is_set():
+ time.sleep(0.5)
\ No newline at end of file
diff --git a/reachy_mini/tests/ok_app/pyproject.toml b/reachy_mini/tests/ok_app/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..58ff6f6df56e0b3a9c433de7d9985a617ff19389
--- /dev/null
+++ b/reachy_mini/tests/ok_app/pyproject.toml
@@ -0,0 +1,18 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+
+[project]
+name = "ok_app"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.8"
+dependencies = [
+ "reachy-mini"
+]
+keywords = ["reachy-mini-app"]
+
+[project.entry-points."reachy_mini_apps"]
+ok_app = "ok_app.main:OkApp"
\ No newline at end of file
diff --git a/reachy_mini/tests/test_analytical_kinematics.py b/reachy_mini/tests/test_analytical_kinematics.py
new file mode 100644
index 0000000000000000000000000000000000000000..810006a99a15321d2cf6fb2720814d2bbc0cb9ff
--- /dev/null
+++ b/reachy_mini/tests/test_analytical_kinematics.py
@@ -0,0 +1,19 @@
+from reachy_mini.kinematics import AnalyticalKinematics
+import numpy as np
+
+def test_analytical_kinematics():
+ ak = AnalyticalKinematics()
+ pose = np.eye(4)
+ sol = ak.ik(pose)
+ assert sol is not None, "IK solution should be found"
+ fk_pose = ak.fk(sol, no_iterations=10)
+ assert np.allclose(fk_pose, pose, atol=1e-2), "FK should match the original pose"
+
+def test_analytical_kinematics_with_yaw():
+ ak = AnalyticalKinematics()
+ pose = np.eye(4)
+ body_yaw = np.pi / 4 # 45 degrees
+ sol = ak.ik(pose, body_yaw=body_yaw)
+ assert sol is not None, "IK solution should be found with body yaw"
+ fk_pose = ak.fk(sol, no_iterations=10)
+ assert np.allclose(fk_pose, pose, atol=1e-2), "FK should match the original pose with body yaw"
\ No newline at end of file
diff --git a/reachy_mini/tests/test_app.py b/reachy_mini/tests/test_app.py
new file mode 100644
index 0000000000000000000000000000000000000000..58eb7c6d9e5d9e958eaba35f6d44617172a66211
--- /dev/null
+++ b/reachy_mini/tests/test_app.py
@@ -0,0 +1,114 @@
+
+import asyncio
+from pathlib import Path
+from threading import Event
+import time
+import pytest
+
+from reachy_mini import ReachyMiniApp
+from reachy_mini.apps import AppInfo, SourceKind
+from reachy_mini.apps.manager import AppManager, AppState
+from reachy_mini.daemon.daemon import Daemon
+from reachy_mini.reachy_mini import ReachyMini
+
+
+@pytest.mark.asyncio
+async def test_app() -> None:
+ class MockApp(ReachyMiniApp):
+ def run(self, reachy_mini: ReachyMini, stop_event: Event) -> None:
+ time.sleep(1) # Simulate some processing time
+
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+
+ stop = Event()
+
+ with ReachyMini(media_backend="no_media") as mini:
+ app = MockApp()
+ app.run(mini, stop)
+
+ await daemon.stop(goto_sleep_on_stop=False)
+
+
+@pytest.mark.asyncio
+async def test_app_manager() -> None:
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+
+ app_mngr = AppManager()
+
+ before_installed_apps = await app_mngr.list_available_apps(SourceKind.INSTALLED)
+
+ app_info = AppInfo(
+ name="ok_app",
+ source_kind=SourceKind.LOCAL,
+ extra={"path": str(Path(__file__).parent / "ok_app")},
+ )
+ await app_mngr.install_new_app(app_info, daemon.logger)
+
+ after_installed_apps = await app_mngr.list_available_apps(SourceKind.INSTALLED)
+
+ assert len(after_installed_apps) == len(before_installed_apps) + 1
+
+ status = await app_mngr.start_app("ok_app", media_backend="no_media")
+ assert status is not None and status.state in (AppState.STARTING, AppState.RUNNING)
+ assert app_mngr.is_app_running()
+ status = await app_mngr.current_app_status()
+ assert status is not None and status.state in (AppState.STARTING, AppState.RUNNING)
+
+ await app_mngr.stop_current_app()
+ assert not app_mngr.is_app_running()
+ status = await app_mngr.current_app_status()
+ assert status is None
+
+ await app_mngr.remove_app("ok_app", daemon.logger)
+ after_uninstalled_apps = await app_mngr.list_available_apps(SourceKind.INSTALLED)
+
+ assert len(after_uninstalled_apps) == len(before_installed_apps)
+
+ await daemon.stop(goto_sleep_on_stop=False)
+
+
+
+@pytest.mark.asyncio
+async def test_faulty_app() -> None:
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+
+ app_mngr = AppManager()
+
+ app_info = AppInfo(
+ name="faulty_app",
+ source_kind=SourceKind.LOCAL,
+ extra={"path": str(Path(__file__).parent / "faulty_app")},
+ )
+ await app_mngr.install_new_app(app_info, daemon.logger)
+
+
+ status = await app_mngr.start_app("faulty_app", media_backend="no_media")
+
+ for _ in range(10):
+ status = await app_mngr.current_app_status()
+ if status is None or status.state in (AppState.STARTING, AppState.RUNNING):
+ await asyncio.sleep(1.0)
+ continue
+ assert status is not None and status.state == AppState.ERROR
+ break
+ else:
+ pytest.fail("Faulty app did not reach ERROR state in time")
+
+ await app_mngr.remove_app("faulty_app", daemon.logger)
+
+ await daemon.stop(goto_sleep_on_stop=False)
diff --git a/reachy_mini/tests/test_audio.py b/reachy_mini/tests/test_audio.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ca28475f0960c0860c04b196c745c166ecbf09f
--- /dev/null
+++ b/reachy_mini/tests/test_audio.py
@@ -0,0 +1,90 @@
+import os
+import time
+import tempfile
+import pytest
+import soundfile as sf
+from reachy_mini.media.media_manager import MediaManager, MediaBackend
+
+@pytest.mark.audio
+def test_play_sound_default_backend() -> None:
+ """Test playing a sound with the default backend."""
+ media = MediaManager(backend=MediaBackend.DEFAULT_NO_VIDEO)
+ # Use a short sound file present in your assets directory
+ sound_file = "wake_up.wav" # Change to a valid file if needed
+ media.play_sound(sound_file)
+ print("Playing sound with default backend...")
+ # Wait a bit to let the sound play (non-blocking backend)
+ time.sleep(2)
+ # No assertion: test passes if no exception is raised.
+ # Sound should be audible if the audio device is correctly set up.
+
+@pytest.mark.audio
+def test_record_audio_and_file_exists() -> None:
+ """Test recording audio and check that the file exists and is not empty."""
+ media = MediaManager(backend=MediaBackend.DEFAULT_NO_VIDEO)
+ duration = 2 # seconds
+ tmpfile = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
+ tmpfile.close()
+ media.start_recording()
+ time.sleep(duration)
+ media.stop_recording()
+ audio = media.get_audio_sample()
+ samplerate = media.get_input_audio_samplerate()
+ if audio is not None:
+ sf.write(tmpfile.name, audio, samplerate)
+ assert os.path.exists(tmpfile.name)
+ assert os.path.getsize(tmpfile.name) > 0
+ # comment the following line if you want to keep the file for inspection
+ os.remove(tmpfile.name)
+ # print(f"Recorded audio saved to {tmpfile.name}")
+
+@pytest.mark.audio
+def test_DoA() -> None:
+ """Test Direction of Arrival (DoA) estimation."""
+ media = MediaManager(backend=MediaBackend.DEFAULT_NO_VIDEO)
+ doa = media.audio.get_DoA()
+ assert doa is not None
+ assert isinstance(doa, tuple)
+ assert len(doa) == 2
+ assert isinstance(doa[0], float)
+ assert isinstance(doa[1], bool)
+
+
+'''
+@pytest.mark.audio_gstreamer
+def test_play_sound_gstreamer_backend() -> None:
+ """Test playing a sound with the GStreamer backend."""
+ media = MediaManager(backend=MediaBackend.GSTREAMER)
+ time.sleep(2) # Give some time for the audio system to initialize
+ # Use a short sound file present in your assets directory
+ sound_file = "wake_up.wav" # Change to a valid file if needed
+ media.play_sound(sound_file)
+ print("Playing sound with GStreamer backend...")
+ # Wait a bit to let the sound play (non-blocking backend)
+ time.sleep(2)
+ # No assertion: test passes if no exception is raised.
+ # Sound should be audible if the audio device is correctly set up.
+'''
+
+@pytest.mark.audio_gstreamer
+def test_record_audio_and_file_exists_gstreamer() -> None:
+ """Test recording audio and check that the file exists and is not empty."""
+ media = MediaManager(backend=MediaBackend.GSTREAMER)
+ duration = 2 # seconds
+ tmpfile = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
+ tmpfile.close()
+ media.start_recording()
+ time.sleep(duration)
+ audio = media.get_audio_sample()
+ assert audio is not None
+ media.stop_recording()
+
+
+def test_no_media() -> None:
+ """Test that methods handle uninitialized media gracefully."""
+ media = MediaManager(backend=MediaBackend.NO_MEDIA)
+
+ assert media.get_frame() is None
+ assert media.get_audio_sample() is None
+ assert media.get_input_audio_samplerate() == -1
+ assert media.get_output_audio_samplerate() == -1
diff --git a/reachy_mini/tests/test_collision.py b/reachy_mini/tests/test_collision.py
new file mode 100644
index 0000000000000000000000000000000000000000..8948d8d6a869453d78e88dd94155e708495852b5
--- /dev/null
+++ b/reachy_mini/tests/test_collision.py
@@ -0,0 +1,17 @@
+from reachy_mini.utils.constants import URDF_ROOT_PATH
+from reachy_mini.kinematics import PlacoKinematics
+from reachy_mini.utils import create_head_pose
+
+
+def offline_test_collision():
+ head_kinematics = PlacoKinematics(
+ urdf_path=URDF_ROOT_PATH, check_collision=True
+ )
+
+ reachable_pose = create_head_pose()
+ sol = head_kinematics.ik(reachable_pose)
+ assert sol is not None, "The reachable pose should not cause a collision."
+
+ unreachable_pose = create_head_pose(x=20, y=20, mm=True)
+ sol = head_kinematics.ik(unreachable_pose)
+ assert sol is None, "The unreachable pose should cause a collision."
diff --git a/reachy_mini/tests/test_daemon.py b/reachy_mini/tests/test_daemon.py
new file mode 100644
index 0000000000000000000000000000000000000000..589f1c09c042a43c335cbdbc555f1961550eb565
--- /dev/null
+++ b/reachy_mini/tests/test_daemon.py
@@ -0,0 +1,92 @@
+import asyncio
+import numpy as np
+import pytest
+
+from reachy_mini.daemon.daemon import Daemon
+from reachy_mini.reachy_mini import ReachyMini
+
+@pytest.mark.asyncio
+async def test_daemon_start_stop() -> None:
+ from reachy_mini.daemon.daemon import Daemon
+
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+ await daemon.stop(goto_sleep_on_stop=False)
+
+
+@pytest.mark.asyncio
+async def test_daemon_multiple_start_stop() -> None:
+ daemon = Daemon()
+
+ for _ in range(3):
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+ await daemon.stop(goto_sleep_on_stop=False)
+
+
+@pytest.mark.asyncio
+async def test_daemon_client_disconnection() -> None:
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+
+ client_connected = asyncio.Event()
+
+ async def simple_client() -> None:
+ with ReachyMini(media_backend="no_media") as mini:
+ status = mini.client.get_status()
+ assert status['state'] == "running"
+ assert status['simulation_enabled']
+ assert status['error'] is None
+ assert status['backend_status']['motor_control_mode'] == "enabled"
+ assert status['backend_status']['error'] is None
+ assert status['wlan_ip'] is None
+ client_connected.set()
+
+ async def wait_for_client() -> None:
+ await client_connected.wait()
+ await daemon.stop(goto_sleep_on_stop=False)
+
+ await asyncio.gather(simple_client(), wait_for_client())
+
+@pytest.mark.asyncio
+async def test_daemon_early_stop() -> None:
+ daemon = Daemon()
+ await daemon.start(
+ sim=True,
+ headless=True,
+ wake_up_on_start=False,
+ )
+
+ client_connected = asyncio.Event()
+ daemon_stopped = asyncio.Event()
+
+ async def client_bg() -> None:
+ with ReachyMini(media_backend="no_media") as reachy:
+ client_connected.set()
+ await daemon_stopped.wait()
+
+ # Make sure the keep-alive check runs at least once
+ reachy.client._check_alive_evt.clear()
+ reachy.client._check_alive_evt.wait(timeout=100.0)
+
+ with pytest.raises(ConnectionError, match="Lost connection with the server."):
+ reachy.set_target(head=np.eye(4))
+
+ async def will_stop_soon() -> None:
+ await client_connected.wait()
+ await daemon.stop(goto_sleep_on_stop=False)
+ daemon_stopped.set()
+
+ await asyncio.gather(client_bg(), will_stop_soon())
+
diff --git a/reachy_mini/tests/test_import.py b/reachy_mini/tests/test_import.py
new file mode 100644
index 0000000000000000000000000000000000000000..a553156690d214bc6e65f88f56c5bbbf7cf6c13b
--- /dev/null
+++ b/reachy_mini/tests/test_import.py
@@ -0,0 +1,6 @@
+
+
+def test_import(): # noqa: D100, D103
+ from reachy_mini import ReachyMini # noqa: F401
+ from reachy_mini import ReachyMiniApp # noqa: F401
+
diff --git a/reachy_mini/tests/test_placo.py b/reachy_mini/tests/test_placo.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f9a76aff6c49311993d963fcb4ebde42b0c5b7e
--- /dev/null
+++ b/reachy_mini/tests/test_placo.py
@@ -0,0 +1,7 @@
+def test_load_kinematics(): # noqa: D100, D103
+ from reachy_mini.utils.constants import URDF_ROOT_PATH
+ from reachy_mini.kinematics import PlacoKinematics
+
+ # Test loading the kinematics
+ kinematics = PlacoKinematics(URDF_ROOT_PATH)
+ assert kinematics is not None, "Failed to load PlacoKinematics."
diff --git a/reachy_mini/tests/test_video.py b/reachy_mini/tests/test_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..63fbccd0bac4dd75e771ec72b818f523461a7350
--- /dev/null
+++ b/reachy_mini/tests/test_video.py
@@ -0,0 +1,87 @@
+from reachy_mini.media.camera_constants import ReachyMiniCamSpecs, ArduCamResolution, MujocoCameraSpecs
+from reachy_mini.media.media_manager import MediaManager, MediaBackend
+import numpy as np
+import pytest
+import time
+# import tempfile
+# import cv2
+
+
+@pytest.mark.video
+def test_get_frame_exists() -> None:
+ """Test that a frame can be retrieved from the camera and is not None."""
+ media = MediaManager(backend=MediaBackend.DEFAULT)
+ frame = media.get_frame()
+ assert frame is not None, "No frame was retrieved from the camera."
+ assert isinstance(frame, np.ndarray), "Frame is not a numpy array."
+ assert frame.size > 0, "Frame is empty."
+ assert frame.shape[0] == media.camera.resolution[1] and frame.shape[1] == media.camera.resolution[0], f"Frame has incorrect dimensions: {frame.shape}"
+
+ # with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file:
+ # cv2.imwrite(tmp_file.name, frame)
+ # print(f"Frame saved for inspection: {tmp_file.name}")
+
+@pytest.mark.video
+def test_get_frame_exists_all_resolutions() -> None:
+ """Test that a frame can be retrieved from the camera for all supported resolutions."""
+ media = MediaManager(backend=MediaBackend.DEFAULT)
+ for resolution in media.camera.camera_specs.available_resolutions:
+ media.camera.set_resolution(resolution)
+ frame = media.get_frame()
+ assert frame is not None, f"No frame was retrieved from the camera at resolution {resolution}."
+ assert isinstance(frame, np.ndarray), f"Frame is not a numpy array at resolution {resolution}."
+ assert frame.size > 0, f"Frame is empty at resolution {resolution}."
+ assert frame.shape[0] == resolution.value[1] and frame.shape[1] == resolution.value[0], f"Frame has incorrect dimensions at resolution {resolution}: {frame.shape}"
+
+@pytest.mark.video
+def test_change_resolution_errors() -> None:
+ """Test that changing resolution raises a runtime error if not allowed."""
+ media = MediaManager(backend=MediaBackend.DEFAULT)
+ media.camera.camera_specs = None
+ with pytest.raises(RuntimeError):
+ media.camera.set_resolution(ArduCamResolution.R1280x720)
+
+ media.camera.camera_specs = MujocoCameraSpecs()
+ with pytest.raises(RuntimeError):
+ media.camera.set_resolution(ArduCamResolution.R1280x720)
+
+ media.camera.camera_specs = ReachyMiniCamSpecs()
+ with pytest.raises(ValueError):
+ media.camera.set_resolution(ArduCamResolution.R1280x720)
+
+
+@pytest.mark.video_gstreamer
+def test_get_frame_exists_gstreamer() -> None:
+ """Test that a frame can be retrieved from the camera and is not None."""
+ media = MediaManager(backend=MediaBackend.GSTREAMER)
+ time.sleep(2) # Give some time for the camera to initialize
+ frame = media.get_frame()
+ assert frame is not None, "No frame was retrieved from the camera."
+ assert isinstance(frame, np.ndarray), "Frame is not a numpy array."
+ assert frame.size > 0, "Frame is empty."
+ assert frame.shape[0] == media.camera.resolution[1] and frame.shape[1] == media.camera.resolution[0], f"Frame has incorrect dimensions: {frame.shape}"
+
+@pytest.mark.video_gstreamer
+def test_get_frame_exists_all_resolutions_gstreamer() -> None:
+ """Test that a frame can be retrieved from the camera for all supported resolutions."""
+ media = MediaManager(backend=MediaBackend.GSTREAMER)
+ time.sleep(2) # Give some time for the camera to initialize
+
+ for resolution in media.camera.camera_specs.available_resolutions:
+ media.camera.close()
+ media.camera.set_resolution(resolution)
+ media.camera.open()
+ time.sleep(2) # Give some time for the camera to adjust to new resolution
+ frame = media.get_frame()
+ assert frame is not None, f"No frame was retrieved from the camera at resolution {resolution}."
+ assert isinstance(frame, np.ndarray), f"Frame is not a numpy array at resolution {resolution}."
+ assert frame.size > 0, f"Frame is empty at resolution {resolution}."
+ assert frame.shape[0] == resolution.value[1] and frame.shape[1] == resolution.value[0], f"Frame has incorrect dimensions at resolution {resolution}: {frame.shape}"
+
+@pytest.mark.video_gstreamer
+def test_change_resolution_errors_gstreamer() -> None:
+ """Test that changing resolution raises a runtime error if not allowed."""
+ media = MediaManager(backend=MediaBackend.GSTREAMER)
+ time.sleep(1) # Give some time for the camera to initialize
+ with pytest.raises(RuntimeError):
+ media.camera.set_resolution(media.camera.camera_specs.available_resolutions[0])
diff --git a/reachy_mini/tests/test_wireless.py b/reachy_mini/tests/test_wireless.py
new file mode 100644
index 0000000000000000000000000000000000000000..0d702d4666d90218d3a0548319ab50e256b824b5
--- /dev/null
+++ b/reachy_mini/tests/test_wireless.py
@@ -0,0 +1,30 @@
+import pytest
+
+
+from reachy_mini.reachy_mini import ReachyMini
+import time
+import numpy as np
+from reachy_mini.media.camera_constants import RPICameraResolution
+
+@pytest.mark.wireless
+def test_daemon_wireless_client_disconnection() -> None:
+ with ReachyMini(media_backend="no_media", localhost_only=False) as mini:
+ status = mini.client.get_status()
+ assert status['state'] == "running"
+ assert status['wireless_version'] is True
+ assert not status['simulation_enabled']
+ assert status['error'] is None
+ assert status['backend_status']['motor_control_mode'] == "enabled"
+ assert status['backend_status']['error'] is None
+ assert isinstance(status['wlan_ip'], str)
+ assert status['wlan_ip'].count('.') == 3
+ assert all(0 <= int(part) <= 255 for part in status['wlan_ip'].split('.') if part.isdigit())
+
+@pytest.mark.wireless_gstreamer
+def test_daemon_wireless_gstreamer() -> None:
+ with ReachyMini(media_backend="gstreamer") as mini:
+ time.sleep(3) # Give some time for the camera to initialize
+ frame = mini.media.get_frame()
+ assert frame is not None, "No frame was retrieved from the camera."
+ assert isinstance(frame, np.ndarray), "Frame is not a numpy array."
+ assert frame.shape[0] == RPICameraResolution.R1280x720.value[1] and frame.shape[1] == RPICameraResolution.R1280x720.value[0], f"Frame has incorrect dimensions: {frame.shape}"
diff --git a/reachy_mini/tools/setup_motor.py b/reachy_mini/tools/setup_motor.py
new file mode 100644
index 0000000000000000000000000000000000000000..32b74b0c1f93bf28756662ebbf71d0343a14e611
--- /dev/null
+++ b/reachy_mini/tools/setup_motor.py
@@ -0,0 +1,389 @@
+"""Motor setup script for the Reachy Mini robot.
+
+This script allows to configure the motors of the Reachy Mini robot by setting their ID, baudrate, offset, angle limits, return delay time, and removing the input voltage error.
+
+The motor needs to be configured one by one, so you will need to connect only one motor at a time to the serial port. You can specify which motor to configure by passing its name as an argument.
+
+If not specified, it assumes the motor is in the factory settings (ID 1 and baudrate 57600). If it's not the case, you will need to use a tool like Dynamixel Wizard to first reset it or manually specify the ID and baudrate.
+
+Please note that all values given in the configuration file are in the motor's raw units.
+"""
+
+import argparse
+import time
+from pathlib import Path
+
+from rustypot import Xl330PyController
+
+from reachy_mini.utils.hardware_config.parser import MotorConfig, parse_yaml_config
+
+FACTORY_DEFAULT_ID = 1
+FACTORY_DEFAULT_BAUDRATE = 57600
+SERIAL_TIMEOUT = 0.01 # seconds
+MOTOR_SETUP_DELAY = 0.1 # seconds
+
+XL_BAUDRATE_CONV_TABLE = {
+ 9600: 0,
+ 57600: 1,
+ 115200: 2,
+ 1000000: 3,
+ 2000000: 4,
+ 3000000: 5,
+ 4000000: 6,
+}
+
+
+def setup_motor(
+ motor_config: MotorConfig,
+ serial_port: str,
+ from_baudrate: int,
+ target_baudrate: int,
+ from_id: int,
+):
+ """Set up the motor with the given configuration."""
+ if not lookup_for_motor(
+ serial_port,
+ from_id,
+ from_baudrate,
+ ):
+ raise RuntimeError(
+ f"No motor found on port {serial_port}. "
+ f"Make sure the motor is in factory settings (ID {from_id} and baudrate {from_baudrate}) and connected to the specified port."
+ )
+
+ # Make sure the torque is disabled to be able to write EEPROM
+ disable_torque(serial_port, from_id, from_baudrate)
+ try:
+ if from_baudrate != target_baudrate:
+ change_baudrate(
+ serial_port,
+ id=from_id,
+ base_baudrate=from_baudrate,
+ target_baudrate=target_baudrate,
+ )
+ time.sleep(MOTOR_SETUP_DELAY)
+
+ if from_id != motor_config.id:
+ change_id(
+ serial_port,
+ current_id=from_id,
+ new_id=motor_config.id,
+ baudrate=target_baudrate,
+ )
+ time.sleep(MOTOR_SETUP_DELAY)
+
+ change_offset(
+ serial_port,
+ id=motor_config.id,
+ offset=motor_config.offset,
+ baudrate=target_baudrate,
+ )
+
+ time.sleep(MOTOR_SETUP_DELAY)
+
+ change_angle_limits(
+ serial_port,
+ id=motor_config.id,
+ angle_limit_min=motor_config.angle_limit_min,
+ angle_limit_max=motor_config.angle_limit_max,
+ baudrate=target_baudrate,
+ )
+
+ time.sleep(MOTOR_SETUP_DELAY)
+
+ change_shutdown_error(
+ serial_port,
+ id=motor_config.id,
+ baudrate=target_baudrate,
+ shutdown_error=motor_config.shutdown_error,
+ )
+
+ time.sleep(MOTOR_SETUP_DELAY)
+
+ change_return_delay_time(
+ serial_port,
+ id=motor_config.id,
+ return_delay_time=motor_config.return_delay_time,
+ baudrate=target_baudrate,
+ )
+
+ time.sleep(MOTOR_SETUP_DELAY)
+ except Exception as e:
+ print(f"Error while setting up motor ID {from_id}: {e}")
+ raise e
+
+
+def lookup_for_motor(
+ serial_port: str, id: int, baudrate: int, silent: bool = False
+) -> bool:
+ """Check if a motor with the given ID is reachable on the specified serial port."""
+ if not silent:
+ print(
+ f"Looking for motor with ID {id} on port {serial_port}...",
+ end="",
+ flush=True,
+ )
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ ret = c.ping(id)
+ if not silent:
+ print(f"{'✅' if ret else '❌'}")
+ return ret
+
+
+def disable_torque(serial_port: str, id: int, baudrate: int):
+ """Disable the torque of the motor with the given ID on the specified serial port."""
+ print(f"Disabling torque for motor with ID {id}...", end="", flush=True)
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_torque_enable(id, False)
+ print("✅")
+
+
+def change_baudrate(
+ serial_port: str, id: int, base_baudrate: int, target_baudrate: int
+):
+ """Change the baudrate of the motor with the given ID on the specified serial port."""
+ print(f"Changing baudrate to {target_baudrate}...", end="", flush=True)
+ c = Xl330PyController(serial_port, baudrate=base_baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_baud_rate(id, XL_BAUDRATE_CONV_TABLE[target_baudrate])
+ print("✅")
+
+
+def change_id(serial_port: str, current_id: int, new_id: int, baudrate: int):
+ """Change the ID of the motor with the given current ID on the specified serial port."""
+ print(f"Changing ID from {current_id} to {new_id}...", end="", flush=True)
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_id(current_id, new_id)
+ print("✅")
+
+
+def change_offset(serial_port: str, id: int, offset: int, baudrate: int):
+ """Change the offset of the motor with the given ID on the specified serial port."""
+ print(f"Changing offset for motor with ID {id} to {offset}...", end="", flush=True)
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_homing_offset(id, offset)
+ print("✅")
+
+
+def change_angle_limits(
+ serial_port: str,
+ id: int,
+ angle_limit_min: int,
+ angle_limit_max: int,
+ baudrate: int,
+):
+ """Change the angle limits of the motor with the given ID on the specified serial port."""
+ print(
+ f"Changing angle limits for motor with ID {id} to [{angle_limit_min}, {angle_limit_max}]...",
+ end="",
+ flush=True,
+ )
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_raw_min_position_limit(id, angle_limit_min)
+ c.write_raw_max_position_limit(id, angle_limit_max)
+ print("✅")
+
+
+def change_shutdown_error(
+ serial_port: str, id: int, baudrate: int, shutdown_error: int
+):
+ """Change the shutdown error of the motor with the given ID on the specified serial port."""
+ print(
+ f"Changing shutdown error for motor with ID {id} to {shutdown_error}...",
+ end="",
+ flush=True,
+ )
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_shutdown(id, shutdown_error)
+ print("✅")
+
+
+def change_return_delay_time(
+ serial_port: str, id: int, return_delay_time: int, baudrate: int
+):
+ """Change the return delay time of the motor with the given ID on the specified serial port."""
+ print(
+ f"Changing return delay time for motor with ID {id} to {return_delay_time}...",
+ end="",
+ flush=True,
+ )
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ c.write_return_delay_time(id, return_delay_time)
+ print("✅")
+
+
+def light_led_up(serial_port: str, id: int, baudrate: int):
+ """Light the LED of the motor with the given ID on the specified serial port."""
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+
+ trials = 0
+
+ while trials < 3:
+ try:
+ c.write_led(id, 1)
+ break
+ except RuntimeError as e:
+ print(f"Error while turning on LED for motor ID {id}: {e}")
+ trials += 1
+
+
+def light_led_down(serial_port: str, id: int, baudrate: int):
+ """Light the LED of the motor with the given ID on the specified serial port."""
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+ trials = 0
+
+ while trials < 3:
+ try:
+ c.write_led(id, 0)
+ break
+ except RuntimeError as e:
+ print(f"Error while turning off LED for motor ID {id}: {e}")
+ trials += 1
+
+
+def check_configuration(motor_config: MotorConfig, serial_port: str, baudrate: int):
+ """Check the configuration of the motor with the given ID on the specified serial port."""
+ c = Xl330PyController(serial_port, baudrate=baudrate, timeout=SERIAL_TIMEOUT)
+
+ print("Checking configuration...")
+
+ # Check if there is a motor with the desired ID
+ if not c.ping(motor_config.id):
+ raise RuntimeError(f"No motor with ID {motor_config.id} found, cannot proceed")
+ print(f"Found motor with ID {motor_config.id} ✅.")
+
+ # Read return delay time
+ return_delay = c.read_return_delay_time(motor_config.id)[0]
+ if return_delay != motor_config.return_delay_time:
+ raise RuntimeError(
+ f"Return delay time is {return_delay}, expected {motor_config.return_delay_time}"
+ )
+ print(f"Return delay time is correct: {return_delay} ✅.")
+
+ # Read angle limits
+ angle_limit_min = c.read_raw_min_position_limit(motor_config.id)[0]
+ angle_limit_max = c.read_raw_max_position_limit(motor_config.id)[0]
+ if angle_limit_min != motor_config.angle_limit_min:
+ raise RuntimeError(
+ f"Angle limit min is {angle_limit_min}, expected {motor_config.angle_limit_min}"
+ )
+ if angle_limit_max != motor_config.angle_limit_max:
+ raise RuntimeError(
+ f"Angle limit max is {angle_limit_max}, expected {motor_config.angle_limit_max}"
+ )
+ print(
+ f"Angle limits are correct: [{motor_config.angle_limit_min}, {motor_config.angle_limit_max}] ✅."
+ )
+
+ # Read homing offset
+ offset = c.read_homing_offset(motor_config.id)[0]
+ if offset != motor_config.offset:
+ raise RuntimeError(f"Homing offset is {offset}, expected {motor_config.offset}")
+ print(f"Homing offset is correct: {offset} ✅.")
+
+ # Read shutdown
+ shutdown = c.read_shutdown(motor_config.id)[0]
+ if shutdown != motor_config.shutdown_error:
+ raise RuntimeError(
+ f"Shutdown is {shutdown}, expected {motor_config.shutdown_error}"
+ )
+ print(f"Shutdown error is correct: {shutdown} ✅.")
+
+ print("Configuration is correct ✅!")
+
+
+def run(args):
+ """Entry point for the Reachy Mini motor configuration tool."""
+ config = parse_yaml_config(args.config_file)
+
+ if args.motor_name == "all":
+ motors = list(config.motors.keys())
+ else:
+ motors = [args.motor_name]
+
+ for motor_name in motors:
+ motor_config = config.motors[motor_name]
+
+ if args.update_config:
+ args.from_id = motor_config.id
+ args.from_baudrate = config.serial.baudrate
+
+ if not args.check_only:
+ setup_motor(
+ motor_config,
+ args.serialport,
+ from_id=args.from_id,
+ from_baudrate=args.from_baudrate,
+ target_baudrate=config.serial.baudrate,
+ )
+
+ try:
+ check_configuration(
+ motor_config,
+ args.serialport,
+ baudrate=config.serial.baudrate,
+ )
+ except RuntimeError as e:
+ print(f"❌ Configuration check failed for motor '{motor_name}': {e}")
+ return
+
+ light_led_up(
+ args.serialport,
+ motor_config.id,
+ baudrate=config.serial.baudrate,
+ )
+
+
+if __name__ == "__main__":
+ """Entry point for the Reachy Mini motor configuration tool."""
+ parser = argparse.ArgumentParser(description="Motor Configuration tool")
+ parser.add_argument(
+ "config_file",
+ type=Path,
+ help="Path to the hardware configuration file (default: hardware_config.yaml).",
+ )
+ parser.add_argument(
+ "motor_name",
+ type=str,
+ help="Name of the motor to configure.",
+ choices=[
+ "body_rotation",
+ "stewart_1",
+ "stewart_2",
+ "stewart_3",
+ "stewart_4",
+ "stewart_5",
+ "stewart_6",
+ "right_antenna",
+ "left_antenna",
+ "all",
+ ],
+ )
+ parser.add_argument(
+ "serialport",
+ type=str,
+ help="Serial port for communication with the motor.",
+ )
+ parser.add_argument(
+ "--check-only",
+ action="store_true",
+ help="Only check the configuration without applying changes.",
+ )
+ parser.add_argument(
+ "--from-id",
+ type=int,
+ default=FACTORY_DEFAULT_ID,
+ help=f"Current ID of the motor (default: {FACTORY_DEFAULT_ID}).",
+ )
+ parser.add_argument(
+ "--from-baudrate",
+ type=int,
+ default=FACTORY_DEFAULT_BAUDRATE,
+ help=f"Current baudrate of the motor (default: {FACTORY_DEFAULT_BAUDRATE}).",
+ )
+ parser.add_argument(
+ "--update-config",
+ action="store_true",
+ help="Update a specific motor (assumes it already has the correct id and baudrate).",
+ )
+ args = parser.parse_args()
+ run(args)
diff --git a/reachy_mini/tools/setup_motor_rpi.py b/reachy_mini/tools/setup_motor_rpi.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d865de339bc1d7bf3032fb26cd6b2d9417ef8c7
--- /dev/null
+++ b/reachy_mini/tools/setup_motor_rpi.py
@@ -0,0 +1,108 @@
+"""You need to `pip install gpiozero lgpio`."""
+
+import argparse
+import os
+import time
+from typing import List
+
+import numpy as np
+from gpiozero import DigitalOutputDevice
+from setup_motor import (
+ FACTORY_DEFAULT_BAUDRATE,
+ FACTORY_DEFAULT_ID,
+ light_led_down,
+ lookup_for_motor,
+ parse_yaml_config,
+ run,
+)
+
+assets_root_path = "../src/reachy_mini/assets/"
+
+
+UART_PORT = "/dev/ttyAMA3"
+CONFIG_FILE_PATH = os.path.join(assets_root_path, "config", "hardware_config.yaml")
+
+ID_TO_CHANNEL = {
+ 10: 0,
+ 11: 1,
+ 12: 2,
+ 13: 3,
+ 14: 4,
+ 15: 5,
+ 16: 6,
+ 17: 7,
+ 18: 8,
+}
+CHANNEL_TO_ID = {v: k for k, v in ID_TO_CHANNEL.items()}
+
+S0 = DigitalOutputDevice(25)
+S1 = DigitalOutputDevice(8)
+S2 = DigitalOutputDevice(7)
+S3 = DigitalOutputDevice(1)
+
+
+def get_channel_binary(channel) -> List[int]:
+ """Convert channel number (0-8) to 4-bit binary representation."""
+ assert channel in np.arange(9), "Channel must be between 0 and 8"
+ bits = [int(b) for b in f"{channel:04b}"] # 4-bit binary
+ return bits[::-1] # flip the order
+
+
+def select_channel(channel: int):
+ """Select a channel on the multiplexer."""
+ bits = get_channel_binary(channel)
+ S0.on() if bits[0] else S0.off()
+ S1.on() if bits[1] else S1.off()
+ S2.on() if bits[2] else S2.off()
+ S3.on() if bits[3] else S3.off()
+
+
+def main():
+ """Scan all channels of the multiplexer to find motors in factory default state, and set them up one by one."""
+ config = parse_yaml_config(CONFIG_FILE_PATH)
+ motor_name_to_id = {m: config.motors[m].id for m in config.motors}
+ id_to_motor_name = {v: k for k, v in motor_name_to_id.items()}
+
+ print("Starting motor setup...")
+ current_channel = 0
+ while True:
+ current_channel = (current_channel + 1) % 9
+ select_channel(current_channel)
+ target_id = CHANNEL_TO_ID[current_channel]
+ target_name = id_to_motor_name[target_id]
+ if lookup_for_motor(
+ UART_PORT, FACTORY_DEFAULT_ID, FACTORY_DEFAULT_BAUDRATE, silent=True
+ ):
+ print(f"Found motor on channel {current_channel}!")
+ args = argparse.Namespace(
+ config_file=CONFIG_FILE_PATH,
+ motor_name=target_name,
+ serialport=UART_PORT,
+ check_only=False,
+ from_id=FACTORY_DEFAULT_ID,
+ from_baudrate=FACTORY_DEFAULT_BAUDRATE,
+ update_config=False,
+ )
+ run(args)
+
+ elif lookup_for_motor(UART_PORT, current_channel + 10, 1000000, silent=True):
+ print(f"Motor on channel {current_channel} already set up.")
+ # light_led_up(UART_PORT, current_channel+10, 1000000)
+ light_led_down(UART_PORT, current_channel + 10, 1000000)
+ args = argparse.Namespace(
+ config_file=CONFIG_FILE_PATH,
+ motor_name=target_name,
+ serialport=UART_PORT,
+ check_only=False,
+ from_id=current_channel + 10,
+ from_baudrate=1000000,
+ update_config=False,
+ )
+ run(args)
+ time.sleep(2)
+
+ time.sleep(0.01)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/reachy_mini/uv.lock b/reachy_mini/uv.lock
new file mode 100644
index 0000000000000000000000000000000000000000..fed1adff9e455914f52d524e9fefff4ea32b141f
--- /dev/null
+++ b/reachy_mini/uv.lock
@@ -0,0 +1,3938 @@
+version = 1
+revision = 3
+requires-python = ">=3.10"
+resolution-markers = [
+ "python_full_version >= '3.13'",
+ "python_full_version >= '3.11' and python_full_version < '3.13'",
+ "python_full_version < '3.11'",
+]
+
+[[package]]
+name = "absl-py"
+version = "2.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" },
+]
+
+[[package]]
+name = "ahrs"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/29/1232576b1f6769bf7d1436e5ae5f7234b56bb7bbc255b68a483165090cf3/ahrs-0.4.0.tar.gz", hash = "sha256:4b632b9e53b9cfd1cecadefa543a3534574baeafeef048db703ab33a07dcca21", size = 530456, upload-time = "2025-10-13T11:05:58.675Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/76/931b879e1ad5e287c4ed9b60ccea807e63e06cd257829e154c79394abb66/ahrs-0.4.0-py3-none-any.whl", hash = "sha256:7be83963d8021ae326d20b646c05a2218401e559f645bb5c2e40ff7dd54245d7", size = 244694, upload-time = "2025-10-13T11:05:57.097Z" },
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.12.15"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "async-timeout", marker = "python_full_version < '3.11'" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/47/dc/ef9394bde9080128ad401ac7ede185267ed637df03b51f05d14d1c99ad67/aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc", size = 703921, upload-time = "2025-07-29T05:49:43.584Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/42/63fccfc3a7ed97eb6e1a71722396f409c46b60a0552d8a56d7aad74e0df5/aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af", size = 480288, upload-time = "2025-07-29T05:49:47.851Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/a2/7b8a020549f66ea2a68129db6960a762d2393248f1994499f8ba9728bbed/aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421", size = 468063, upload-time = "2025-07-29T05:49:49.789Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/f5/d11e088da9176e2ad8220338ae0000ed5429a15f3c9dfd983f39105399cd/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79", size = 1650122, upload-time = "2025-07-29T05:49:51.874Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6b/b60ce2757e2faed3d70ed45dafee48cee7bfb878785a9423f7e883f0639c/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77", size = 1624176, upload-time = "2025-07-29T05:49:53.805Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/de/8c9fde2072a1b72c4fadecf4f7d4be7a85b1d9a4ab333d8245694057b4c6/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c", size = 1696583, upload-time = "2025-07-29T05:49:55.338Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ad/07f863ca3d895a1ad958a54006c6dafb4f9310f8c2fdb5f961b8529029d3/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4", size = 1738896, upload-time = "2025-07-29T05:49:57.045Z" },
+ { url = "https://files.pythonhosted.org/packages/20/43/2bd482ebe2b126533e8755a49b128ec4e58f1a3af56879a3abdb7b42c54f/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6", size = 1643561, upload-time = "2025-07-29T05:49:58.762Z" },
+ { url = "https://files.pythonhosted.org/packages/23/40/2fa9f514c4cf4cbae8d7911927f81a1901838baf5e09a8b2c299de1acfe5/aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2", size = 1583685, upload-time = "2025-07-29T05:50:00.375Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/c3/94dc7357bc421f4fb978ca72a201a6c604ee90148f1181790c129396ceeb/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d", size = 1627533, upload-time = "2025-07-29T05:50:02.306Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/3f/1f8911fe1844a07001e26593b5c255a685318943864b27b4e0267e840f95/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb", size = 1638319, upload-time = "2025-07-29T05:50:04.282Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/46/27bf57a99168c4e145ffee6b63d0458b9c66e58bb70687c23ad3d2f0bd17/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5", size = 1613776, upload-time = "2025-07-29T05:50:05.863Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/7e/1d2d9061a574584bb4ad3dbdba0da90a27fdc795bc227def3a46186a8bc1/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b", size = 1693359, upload-time = "2025-07-29T05:50:07.563Z" },
+ { url = "https://files.pythonhosted.org/packages/08/98/bee429b52233c4a391980a5b3b196b060872a13eadd41c3a34be9b1469ed/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065", size = 1716598, upload-time = "2025-07-29T05:50:09.33Z" },
+ { url = "https://files.pythonhosted.org/packages/57/39/b0314c1ea774df3392751b686104a3938c63ece2b7ce0ba1ed7c0b4a934f/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1", size = 1644940, upload-time = "2025-07-29T05:50:11.334Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/83/3dacb8d3f8f512c8ca43e3fa8a68b20583bd25636ffa4e56ee841ffd79ae/aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a", size = 429239, upload-time = "2025-07-29T05:50:12.803Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/f9/470b5daba04d558c9673ca2034f28d067f3202a40e17804425f0c331c89f/aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830", size = 452297, upload-time = "2025-07-29T05:50:14.266Z" },
+ { url = "https://files.pythonhosted.org/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117", size = 711246, upload-time = "2025-07-29T05:50:15.937Z" },
+ { url = "https://files.pythonhosted.org/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe", size = 483515, upload-time = "2025-07-29T05:50:17.442Z" },
+ { url = "https://files.pythonhosted.org/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9", size = 471776, upload-time = "2025-07-29T05:50:19.568Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5", size = 1741977, upload-time = "2025-07-29T05:50:21.665Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728", size = 1690645, upload-time = "2025-07-29T05:50:23.333Z" },
+ { url = "https://files.pythonhosted.org/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16", size = 1789437, upload-time = "2025-07-29T05:50:25.007Z" },
+ { url = "https://files.pythonhosted.org/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0", size = 1828482, upload-time = "2025-07-29T05:50:26.693Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b", size = 1730944, upload-time = "2025-07-29T05:50:28.382Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd", size = 1668020, upload-time = "2025-07-29T05:50:30.032Z" },
+ { url = "https://files.pythonhosted.org/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8", size = 1716292, upload-time = "2025-07-29T05:50:31.983Z" },
+ { url = "https://files.pythonhosted.org/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50", size = 1711451, upload-time = "2025-07-29T05:50:33.989Z" },
+ { url = "https://files.pythonhosted.org/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676", size = 1691634, upload-time = "2025-07-29T05:50:35.846Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7", size = 1785238, upload-time = "2025-07-29T05:50:37.597Z" },
+ { url = "https://files.pythonhosted.org/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7", size = 1805701, upload-time = "2025-07-29T05:50:39.591Z" },
+ { url = "https://files.pythonhosted.org/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685", size = 1718758, upload-time = "2025-07-29T05:50:41.292Z" },
+ { url = "https://files.pythonhosted.org/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b", size = 428868, upload-time = "2025-07-29T05:50:43.063Z" },
+ { url = "https://files.pythonhosted.org/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d", size = 453273, upload-time = "2025-07-29T05:50:44.613Z" },
+ { url = "https://files.pythonhosted.org/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" },
+ { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" },
+ { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" },
+ { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" },
+ { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" },
+ { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" },
+ { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" },
+ { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/33/918091abcf102e39d15aba2476ad9e7bd35ddb190dcdd43a854000d3da0d/aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315", size = 696741, upload-time = "2025-07-29T05:51:19.021Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/2a/7495a81e39a998e400f3ecdd44a62107254803d1681d9189be5c2e4530cd/aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd", size = 474407, upload-time = "2025-07-29T05:51:21.165Z" },
+ { url = "https://files.pythonhosted.org/packages/49/fc/a9576ab4be2dcbd0f73ee8675d16c707cfc12d5ee80ccf4015ba543480c9/aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4", size = 466703, upload-time = "2025-07-29T05:51:22.948Z" },
+ { url = "https://files.pythonhosted.org/packages/09/2f/d4bcc8448cf536b2b54eed48f19682031ad182faa3a3fee54ebe5b156387/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7", size = 1705532, upload-time = "2025-07-29T05:51:25.211Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/f3/59406396083f8b489261e3c011aa8aee9df360a96ac8fa5c2e7e1b8f0466/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d", size = 1686794, upload-time = "2025-07-29T05:51:27.145Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/71/164d194993a8d114ee5656c3b7ae9c12ceee7040d076bf7b32fb98a8c5c6/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b", size = 1738865, upload-time = "2025-07-29T05:51:29.366Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/00/d198461b699188a93ead39cb458554d9f0f69879b95078dce416d3209b54/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d", size = 1788238, upload-time = "2025-07-29T05:51:31.285Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b8/9e7175e1fa0ac8e56baa83bf3c214823ce250d0028955dfb23f43d5e61fd/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d", size = 1710566, upload-time = "2025-07-29T05:51:33.219Z" },
+ { url = "https://files.pythonhosted.org/packages/59/e4/16a8eac9df39b48ae102ec030fa9f726d3570732e46ba0c592aeeb507b93/aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645", size = 1624270, upload-time = "2025-07-29T05:51:35.195Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f8/cd84dee7b6ace0740908fd0af170f9fab50c2a41ccbc3806aabcb1050141/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461", size = 1677294, upload-time = "2025-07-29T05:51:37.215Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/42/d0f1f85e50d401eccd12bf85c46ba84f947a84839c8a1c2c5f6e8ab1eb50/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9", size = 1708958, upload-time = "2025-07-29T05:51:39.328Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/6b/f6fa6c5790fb602538483aa5a1b86fcbad66244997e5230d88f9412ef24c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d", size = 1651553, upload-time = "2025-07-29T05:51:41.356Z" },
+ { url = "https://files.pythonhosted.org/packages/04/36/a6d36ad545fa12e61d11d1932eef273928b0495e6a576eb2af04297fdd3c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693", size = 1727688, upload-time = "2025-07-29T05:51:43.452Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c8/f195e5e06608a97a4e52c5d41c7927301bf757a8e8bb5bbf8cef6c314961/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64", size = 1761157, upload-time = "2025-07-29T05:51:45.643Z" },
+ { url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050, upload-time = "2025-07-29T05:51:48.203Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647, upload-time = "2025-07-29T05:51:50.718Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067, upload-time = "2025-07-29T05:51:52.549Z" },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
+]
+
+[[package]]
+name = "asgiref"
+version = "3.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" },
+]
+
+[[package]]
+name = "asttokens"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" },
+]
+
+[[package]]
+name = "async-timeout"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "25.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
+]
+
+[[package]]
+name = "backports-asyncio-runner"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.8.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
+ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
+ { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
+ { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
+ { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
+ { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
+ { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" },
+ { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" },
+ { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" },
+ { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" },
+ { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" },
+ { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" },
+ { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" },
+ { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" },
+ { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" },
+ { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
+ { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
+ { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
+ { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
+ { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
+ { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
+ { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
+ { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
+ { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
+ { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
+ { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
+ { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
+ { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
+ { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
+]
+
+[[package]]
+name = "cmeel"
+version = "0.57.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6e/ef/0990977bdcb1ffde571f8a0d70c8ad350abe4e398cc09fecabb3e13a8954/cmeel-0.57.3.tar.gz", hash = "sha256:7794a5c3d3365b5ed1db8ab0c152fabfc73af79a3edd6ff8bc2f1405c667dd7f", size = 14884, upload-time = "2025-03-19T15:29:10.298Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/df/634f09c1d2712a6e7b804a5630b0f16f219f6f9c8e1286f3265ca8bdf1ca/cmeel-0.57.3-py3-none-any.whl", hash = "sha256:231d24bb97beae67e3682f026a53e49e227c174e6a3329a9dcb850e5566529b0", size = 20986, upload-time = "2025-03-19T15:29:09.145Z" },
+]
+
+[[package]]
+name = "cmeel-assimp"
+version = "5.4.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-zlib" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/79/ac/f01a4e8c93e7e76f04788bad60bc5eaf7d4862eef24c11dd38bc8b61e819/cmeel_assimp-5.4.3.1.tar.gz", hash = "sha256:bd663a65ed143e71fe4723e787cbf1480e42cea4d2450c15cb5d9d4223d24f3d", size = 53703174, upload-time = "2025-02-11T15:04:22.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/eb/ed8bdfb61ac66a71f623900a7288b465230f6735120689529846c47131fa/cmeel_assimp-5.4.3.1-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:cfd834c910402d0a40ec677ec46f2ad25f096f0146faa23b27d9757fa51adb46", size = 10403810, upload-time = "2025-02-11T15:03:46.824Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/27/fed4d700c3bb35a86a2e563da3ad2ef26e00fc35da6e152ff82cbd022c97/cmeel_assimp-5.4.3.1-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9aa68541f68df3ad64c29a1195c154e0a00e33daaf59a5a7ceb1a6f34e69842f", size = 9530204, upload-time = "2025-02-11T15:03:50.44Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/f1/085c683b6d2871f6a3ba3c3159d73042231be7e71a63c251b9e919fed1b8/cmeel_assimp-5.4.3.1-0-py3-none-manylinux_2_17_i686.whl", hash = "sha256:673fdeef00e0a018c4b5d21345dca06769b693902a48060bb6b7e53b6b65a0f7", size = 14047356, upload-time = "2025-02-11T15:03:56.314Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/85/816c94a00eea68faac68f613f169c0f7023e407b6320193521fccb156e35/cmeel_assimp-5.4.3.1-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6be2a9b69a8ccfce575a2b42fb956c0b6c3e3ec2140f63a5cfa194a9d848449d", size = 13367287, upload-time = "2025-02-11T15:03:59.441Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/ca/2a90a07d23f0c540fdcc1b5a50e3b424760db880e13bdf246e8b35abfaba/cmeel_assimp-5.4.3.1-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:ec259b1aad47c3f5b386004570b65b2b06a4d2a4d00b9959469e5528d029b31b", size = 14445192, upload-time = "2025-02-11T15:04:03.823Z" },
+ { url = "https://files.pythonhosted.org/packages/72/08/d3c3c5efbcb7039892a79efed07439716107768f261eed7c1bfd01a50f11/cmeel_assimp-5.4.3.1-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d43933475fb0d76eee49de6b9a6d8e70492a89308afbde4d17ee4666fc15cfc0", size = 13396207, upload-time = "2025-02-11T15:04:08.58Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/36/9eef6d28a693ea021186d3456abfe98c5eafb5a9f4b6952b726309550f41/cmeel_assimp-5.4.3.1-0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2a22037a03d8756018c659e06ca2273b51dd1f67f057642df28e6f475cd798a8", size = 14795228, upload-time = "2025-02-11T15:04:12.785Z" },
+ { url = "https://files.pythonhosted.org/packages/50/fe/9235a07a66d6d3bc1901360a17ed40b97b0d3904d7cc9f1ac2cc80661bf9/cmeel_assimp-5.4.3.1-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2c4ef463d29a30190262c1e48660d07aab744b583f90deccce24c1fecbf442b5", size = 14285514, upload-time = "2025-02-11T15:04:16.198Z" },
+]
+
+[[package]]
+name = "cmeel-boost"
+version = "1.87.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/ee/a4c9eff9efe87945b770a3b773630cd72cb5f178de2daec719507e0f6024/cmeel_boost-1.87.0.1.tar.gz", hash = "sha256:c1b706e1f0c878693a96f6c92072596dcdf27bd14833b1cbbffd65a8b7e8d00f", size = 4075, upload-time = "2025-02-13T03:46:52.858Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/9c/f13fd327659b49464cc87b9134d70c7d98d833dabbec3a8e50d8e4867422/cmeel_boost-1.87.0.1-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8a3a6c1646c6b4cc0b5b7d108b5f83c26b7ec9b0b63b6924e9f3373cca76d8ea", size = 30514323, upload-time = "2025-02-13T03:37:37.054Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/39/b5e7b972ac2c80ae98f19af2bb0abbbe7321283b19dbc75c1b2becbf0ea8/cmeel_boost-1.87.0.1-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e1b325137363c9dd6b36fd3e21cb3d6386273bc67990b23372c99ccc12648a2", size = 30405820, upload-time = "2025-02-13T03:43:49.338Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/71/83db946b7aa223205b104d47bf395e55324ac03ab3593e0c291dd290997b/cmeel_boost-1.87.0.1-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:33b1c5dd758d0ec32973519c1e3a99fcf492c5d5f0c12ed4b5958b6c74fc72e9", size = 35647645, upload-time = "2025-02-13T03:43:56.397Z" },
+ { url = "https://files.pythonhosted.org/packages/23/d3/4a0d6493fa9de78296aa0549ad2b25ea856f93469ddb470c72cf8df62b18/cmeel_boost-1.87.0.1-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a7939a286ffb85d286f8110678fc6bebf329e2d2c1e8c185735719838288e0ce", size = 35941593, upload-time = "2025-02-13T03:44:03.163Z" },
+ { url = "https://files.pythonhosted.org/packages/48/47/bd07149dbbdc9b25f9e1e6028edada2534a053db4075cf230feba6bc5f2f/cmeel_boost-1.87.0.1-0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5a20ae91166381b7eb38b05c315257910f288bcdf36e6a4f13e0c22cfff86557", size = 35983814, upload-time = "2025-02-13T03:44:09.419Z" },
+ { url = "https://files.pythonhosted.org/packages/83/29/8cf019d94c17e99987541dfac4fc71125160ba092d66b1534eba38f9c66d/cmeel_boost-1.87.0.1-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fe63ed4275dfa22f7d23d76074b6e2ef3e02554365a1ba4cf75ebd45e714c4e7", size = 30514344, upload-time = "2025-02-13T03:44:15.034Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5e/9b097f3b07df2f07d280b2ced9a979867df61f719e808293c5e104fc81c0/cmeel_boost-1.87.0.1-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28d0edd1b49a76563d07b9523fd3d7b37ad54f13644d2507c84e12fc3ba140f5", size = 30405860, upload-time = "2025-02-13T03:44:20.324Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/62/d1a88b84e45b3e40009375b43bd103b511899f5070da342a8e71d4d2a12b/cmeel_boost-1.87.0.1-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e0577a13e99187a66859daa2a873ec5cda852341c27219eb8bbbff6c0be59476", size = 35647178, upload-time = "2025-02-13T03:44:26.095Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/75/0b56a0cdfc9e12e1436a24e164f83d4358d89fce73290f32f937e24fbbc2/cmeel_boost-1.87.0.1-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:4d8f490c3dd7f09a2ff18d8273805371d39efba02ccad711c0417eaba22a5cd8", size = 35941273, upload-time = "2025-02-13T03:44:33.076Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/34/5521df709a2337f67a965fd2c5bde52b27c6ccd1a46e54aa8169206ed52a/cmeel_boost-1.87.0.1-0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8309755cf220a7f76cb019253c211a7d40cc98b04258b5979af1125f5f24b2df", size = 35983443, upload-time = "2025-02-13T03:44:39.667Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/9e/c53b934ee85485366388d5b3902f92ff4c54ce091058c20a92908e1402dc/cmeel_boost-1.87.0.1-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8c6ad4bc187152e44caa9f9952c32fa12b3f03d9d6087bff1071c46ee7c84c4", size = 30518387, upload-time = "2025-02-13T03:44:45.267Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/7d/751d9fb25f2f29e1bb247c7ddc79cd8d2bc41678a32b413968443c5904eb/cmeel_boost-1.87.0.1-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec7a59c722794ddc205b541ed7abe237b72d2fdfdbfe971defbd1920bd58d42e", size = 30407414, upload-time = "2025-02-13T03:44:51.471Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/00/5df4db6c596a4da8c8e35c76af79ea8c0d00badee2d64d8baf56659bca79/cmeel_boost-1.87.0.1-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d3bd5a9855cda2ae8402b487fe028649a50c241e975bce765f33c87dba07324e", size = 35648012, upload-time = "2025-02-13T03:44:57.398Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/48/d4145d17b4abede4df4300b49749efe00e00aae4b654b799dc6c4895e177/cmeel_boost-1.87.0.1-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:fd09a404663bbf610d0a6d786e0ed53a656f2d3dd50b30cf73324b982dd04753", size = 35946214, upload-time = "2025-02-13T03:45:03.378Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/10/d405fed8b0157a6fb7100f9dc883d5cd0cdc3840fa0fa652bb5ca7654a5d/cmeel_boost-1.87.0.1-0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bf5bbe225134829ee57b8d3e0941fcc6868f921bf1b00d02ca9456bcf6cd006", size = 35988693, upload-time = "2025-02-13T03:45:09.774Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/56/3b9e23dd342bf1f5f141575a5bfbf08fe1577d1d1a14217dc2e757bd3cdd/cmeel_boost-1.87.0.1-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3417b8bd016d9e4ddd01afdb7c9cc8fc9806d56e569801b64d90d79a81955727", size = 30518407, upload-time = "2025-02-13T03:45:16.622Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0f/d97add23b2f0708cb48d06b4d4cc3c006983491390b84396424b777eb5f5/cmeel_boost-1.87.0.1-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9193cbb28c4dd5e61f7d0ca472333fb280685ec754813ef15a60bd1bf3ac566f", size = 30407454, upload-time = "2025-02-13T03:45:24.561Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/db157dced18cc57429e4646039e9a54a8d817499666db9984182a30c58e9/cmeel_boost-1.87.0.1-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:84aa37b8131050f5a291ebf44c9d92bb6f45102a082aad30dcc4e2cc01be250c", size = 35648325, upload-time = "2025-02-13T03:45:30.698Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/69/3250e0e3957c36e660bc49add1bdb61f52ab5af32fb0876647b4364f624d/cmeel_boost-1.87.0.1-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b947b10632518066b2cd6fae72cbb8e251a15e76dbeef221e99eeeb61d4b50f2", size = 35946299, upload-time = "2025-02-13T03:45:38.901Z" },
+ { url = "https://files.pythonhosted.org/packages/62/de/8c12c0ea36ac9c17302289453f2e1f99f679163cf7ccdc5f27e12d18cba4/cmeel_boost-1.87.0.1-0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bcf7c2ef54898cf52bea781907262d86f884f5c26de9e8ce125220e062e83113", size = 35988899, upload-time = "2025-02-13T03:45:45.468Z" },
+]
+
+[[package]]
+name = "cmeel-console-bridge"
+version = "1.0.2.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/83/13/2e9e9d23db8548aef975564055bdb4fb6da8a397a1e7df8cb61f5afebefb/cmeel_console_bridge-1.0.2.3.tar.gz", hash = "sha256:3b2837da7ab408e9d1a775c83c0a7772356062b3a3672e4ce247f2da71a8ecd9", size = 262061, upload-time = "2025-03-19T18:22:06.845Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/a7/527fa060e5881acb3b0a07bf1d803ccb831cb87739abb62b6bcd14f5aed3/cmeel_console_bridge-1.0.2.3-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:7aa19b2d006073a1fad55d32968c7d0c7136749e06f98405f4f73a71038a5c41", size = 21341, upload-time = "2025-03-19T18:21:56.834Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/db/f8643a8766e8909e0dbfcda6191ca92454cf9a3fadd89be417db261601a1/cmeel_console_bridge-1.0.2.3-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c47d8c97cb120feed1c01f30845d16c67e4e8205941e3977951018972b9b8721", size = 21286, upload-time = "2025-03-19T18:21:57.984Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/b4/9c79177152a220ab2e4ffa0140722165035f6a5c2abbed2912352bd7e7b9/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_17_i686.whl", hash = "sha256:cad9723ac44ab563cd23bf361b604733623d11847c4edf2a2b4ebd1d984ade09", size = 23740, upload-time = "2025-03-19T18:21:59.683Z" },
+ { url = "https://files.pythonhosted.org/packages/92/65/5741de6f550fe701d0780546d97b283306676315a3e1f379a6038e8c0ab0/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:372942e9c44f681bfff377fba25b348801283aa6f3826a00e4195089bda9737a", size = 25762, upload-time = "2025-03-19T18:22:01.055Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a5/70e23c5570506bb39b56aa4d0f3a4a414e38082ddb33e86a48b546620121/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5bb1115ed38441b2396e732e10ec63d1e68445674f9f5d321f7985eb10e9aeef", size = 24477, upload-time = "2025-03-19T18:22:02.091Z" },
+ { url = "https://files.pythonhosted.org/packages/47/36/bfd5a255348902e39243ccc6eba693bce714b891cd3be5603a9bd50c6de5/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b8d084b797f592942208c2040b08e06b82f8832aa6c5e582ba6f1a4a653505b", size = 24970, upload-time = "2025-03-19T18:22:03.075Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/02/3ae074e9acb9e150a4d5d97f341c2064573cd5fe9e5af20ab58bf8c0020a/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fb6753a9864217d969c4965389d66a476ac978136c03eadf1063b1619c359220", size = 24689, upload-time = "2025-03-19T18:22:04.084Z" },
+ { url = "https://files.pythonhosted.org/packages/69/d0/321f74b7d4167a6c59bb7714a6899ba402d9fad611f62573b9d646107320/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9d446c0fc541413d8d2ceea3c1cfb9cbfd57938d6659c113121eca6c245caafe", size = 24404, upload-time = "2025-03-19T18:22:05.232Z" },
+]
+
+[[package]]
+name = "cmeel-octomap"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/89/ab/2fed2dbee13e4b39949591685419f1dbb691295e32a6bbbaf87edc005922/cmeel_octomap-1.10.0.tar.gz", hash = "sha256:bd79d1d17adede534de242e42e13ef0d9f04bdd27daf7d56c57f7c43670c9b05", size = 1694189, upload-time = "2025-01-06T17:57:05.477Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/22/ea67d35df31ec4bb2ed6e594b173c572c72dbd2a87e96906eac67b4af930/cmeel_octomap-1.10.0-4-py3-none-macosx_14_0_arm64.whl", hash = "sha256:c116eb151920d26ee2b2c1f656cd7526862006739817205f11f9366ab0ef6cb4", size = 639956, upload-time = "2025-01-06T17:56:56.826Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/da/07725a8c11224881f536ad252e97a3d9801b48e5e776017d5f00fb39b17f/cmeel_octomap-1.10.0-4-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:76cc42553f54bae97584aaf0c7bc33753ff287e2738aa2ecac4820121101dd46", size = 1044402, upload-time = "2025-01-06T17:56:58.553Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/15/9617b7039afd6d17d3148f6f970d953f5e265d7736f8fdbca09c86e976a0/cmeel_octomap-1.10.0-4-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5fdb04546fff3accac5f8626c3fc15c3b99e94ab887793565e0b92cedaf96468", size = 1105037, upload-time = "2025-01-06T17:57:01.287Z" },
+ { url = "https://files.pythonhosted.org/packages/51/69/88c1d1eca1abf2387ee8263ac7e12708c8b1b5b70b46a0bd9f43b485165b/cmeel_octomap-1.10.0-4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:84a7376cfced954bb7e3e347afbd02bdc1c83066b995afbdd0fb1e2d9f57ebec", size = 1108359, upload-time = "2025-01-06T17:57:04.085Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/59/57b3b38cf7a382855902b9d24266c283c29d977706438e6b7af62df74e2b/cmeel_octomap-1.10.0-5-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:042b4a21b5e5e19ee78a9a7db78e1b06fb8a287c832031788aec0d3fcabbfecd", size = 748832, upload-time = "2025-02-12T11:57:34.252Z" },
+ { url = "https://files.pythonhosted.org/packages/55/8b/f5ec7676808a48c0185e216c0da700e34cb13ba233f13a4557a5ec56324a/cmeel_octomap-1.10.0-5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:79c15a0ece5ca3746170088ef2a377dfb3df8326fafde9bdba688852219758b9", size = 706924, upload-time = "2025-02-12T11:57:35.846Z" },
+ { url = "https://files.pythonhosted.org/packages/09/50/56de5a4d9f8ca58100146f16f42c4e2fbb49c0957bfe40d3fd2bc910afe4/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_17_i686.whl", hash = "sha256:e2923bf593ebdafed86b6f3890a122c62fbd9cc9f325d60dbecb72b6b60d78fb", size = 1073973, upload-time = "2025-02-12T11:57:37.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/f5/8dddf5cdd31176288acd85cc8bf0262b7c3de81d5cb2cb33aa6646f44eb1/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d9e6f9c826905e8de632e9df8cc20e59ce2eb5d1e0b368d8d4abbbc5c0829c1a", size = 1044533, upload-time = "2025-02-12T11:57:39.672Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/14/b85bd33bb05c9bb7e87b9ac8401793c12a80a6d594b3ca4bcb5e971a24b7/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:b0b54fac180dce4f483afe7029c29cc55f6f2b21be8413e8e2275845b0c204d7", size = 1105199, upload-time = "2025-02-12T11:57:41.286Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ee/fe3360441159974ebdbb4c013a92ad0425d5f8bf414868d5161060e40660/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c8691e665bab7c12b6f51e6c5fbbb83ee6f91dce9d15d9d0387553950e7fb5ee", size = 1092962, upload-time = "2025-02-12T11:57:43.297Z" },
+ { url = "https://files.pythonhosted.org/packages/82/a6/074166544cc0ce3a5d7844f97dfd13d1b3ec7bff6a6e2cfb18d66a671a7f/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:735c0ad84dacbbcc8c4237f127c57244c236b7d6c7500b5c45a4c225e19daac1", size = 1083321, upload-time = "2025-02-12T11:57:46.121Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/a3/b19ea0d30837369091141b248936b0757ee17f58b809007399bad0b398e4/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f86a83f6bd60de290cd327f0374d525328369e76591e3ab2ad1bc0b183678c4", size = 1109207, upload-time = "2025-02-12T11:57:48.709Z" },
+]
+
+[[package]]
+name = "cmeel-qhull"
+version = "8.0.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/51/dd/8d0bcfb18771b2ea02bf85dfbbc587c97b274496fb5419b72134eb69430b/cmeel_qhull-8.0.2.1.tar.gz", hash = "sha256:68e8d41d95f61830f2d460af1e4d760f0dbe4d46413d7c736f0ed701153ebe52", size = 1308055, upload-time = "2023-11-17T14:21:06.003Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/b4/d72ebd5e9ee711b68ad466e7bd4c0edcb45b0c2c8a358fdcdb64b092666a/cmeel_qhull-8.0.2.1-0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:39f5183a6e026754c3c043239bac005bf1825240d72e1d8fdf090a0f3ea27307", size = 2804225, upload-time = "2023-11-17T14:15:39.958Z" },
+ { url = "https://files.pythonhosted.org/packages/29/dc/4bfb8d51a09401cf740e66d10bdb388eacd7c73bae12ef78149cbbc93e83/cmeel_qhull-8.0.2.1-0-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:f135c5a4f4c8ed53f061bc86b794aaca2c0c34761c9269c06b71329c9da56f82", size = 2972481, upload-time = "2023-11-17T14:20:58.418Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/7c/74b5c781cbfc8e4a9bb73b71659cc595bc0163223fd700b18133dbcf2831/cmeel_qhull-8.0.2.1-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:17f519106df79aed9fc5ec92833d4958d132d23021f02a78a9564cdf83a36c7c", size = 3078962, upload-time = "2023-11-17T14:21:00.183Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/16/ef7b6201835ba2492753c9c91b266d047b6664507be42ec858e2b24673b5/cmeel_qhull-8.0.2.1-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:c513abafa40e2b8eb7cd3640e3f92d5391fbd6ec0f4182dbf9536934d8a8ea3e", size = 3194917, upload-time = "2023-11-17T14:21:01.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/ae/200bdf257507e2c95d0656bf02278cd666d49f0a9e2e6d281ea76d7d085c/cmeel_qhull-8.0.2.1-0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:20a69cb34b6250aee1f018412989734c9ddcad6ff66717a7c5516fc19f55d5ff", size = 3290068, upload-time = "2023-11-17T14:21:03.828Z" },
+ { url = "https://files.pythonhosted.org/packages/01/1b/de3fa6091ef58ab40f02653e777c8943acf7cec486184d6007885123571d/cmeel_qhull-8.0.2.1-1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b5d47b113c1cb8f519bc813cf015d0d01f8ce5b08912733a24a6018f7caa6e96", size = 2902499, upload-time = "2025-02-12T11:51:16.999Z" },
+ { url = "https://files.pythonhosted.org/packages/05/0c/5e5d9a033c683eb272508ccf560c03ac6bf5d397b038fe05f896a2283eaf/cmeel_qhull-8.0.2.1-1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:33a0169f4ee37d093c450195b0ef73d4fe0d9d62abb7899ebe79f778b36e1f36", size = 2773563, upload-time = "2025-02-12T11:51:19.893Z" },
+ { url = "https://files.pythonhosted.org/packages/52/9b/00c73069348e60fbbdf6a5a10de046083f7d1ad36844958bbf12163ac688/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_17_i686.whl", hash = "sha256:a577e76ac94d128f2966b137ead9f088749513df63749728e2b588f4564b7fdf", size = 3228684, upload-time = "2025-02-12T11:51:21.888Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/4a/81b8c88b444935a64d8c83b41e662f696c36dd5937c3ca687113ac4778d0/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:fd0b2d4ce749b102c3cdead4588249befd34f1a660628f6bfc090ce942925aac", size = 3156051, upload-time = "2025-02-12T11:51:24.594Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/c1/44874cd8bfc1e3f7cb15678c836c7a1d5537f34f5a727a0207e01f395598/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2371a7c80a14f3e874876359ae3e3094861f081fcdd7a03987c3e880d14e07b9", size = 3262508, upload-time = "2025-02-12T11:51:27.147Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0e/425d9ce1f2a831025d39fa5b6479b856bd4d73614c9caa690ac72bbfca04/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:197c14c2006dbeba8f5a5771700a7afea72c1a441aab7cdeaaf10b4ed8c1137d", size = 3172646, upload-time = "2025-02-12T11:51:28.967Z" },
+ { url = "https://files.pythonhosted.org/packages/00/c1/e973e287a7d793911b8e6497b17586e601a678f2379ba2c615f72bd76480/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:886d1be24b31842286ae42755af5c312a43a4199632826e4110185ec36dc5c6a", size = 3530837, upload-time = "2025-02-12T11:51:31.651Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/65/c6cd54f04b5fcaa4ec52f5b57692c1dcef812ff9ee86545e5607369d365e/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a49ce7f8492c9a8b49f930e34cce75b5e9b9843b015033dd0a25421441159fc", size = 3301908, upload-time = "2025-02-12T11:51:34.53Z" },
+]
+
+[[package]]
+name = "cmeel-tinyxml2"
+version = "10.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/28/9f/030eca702c485f7a641f975f167fa93164911b3329f005fb0730ff5e793f/cmeel_tinyxml2-10.0.0.tar.gz", hash = "sha256:00252aefc1c94a55b89f25ad08ee79fda2da8d1d94703e051598ddb52a9088fe", size = 645297, upload-time = "2025-02-06T10:29:00.106Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/5d/bc3a932eb7996a0a789979426a9bb8a3948bf57f3f17bab87dddbef62433/cmeel_tinyxml2-10.0.0-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:924499bb1b60b9a17bd001d12a9af88ddbee4ca888638ae684ba7f0f3ce49e87", size = 111913, upload-time = "2025-02-06T10:28:45.723Z" },
+ { url = "https://files.pythonhosted.org/packages/92/bf/67d11e123313c034712896e94038291fe506bb099bdb75a136392002ffd0/cmeel_tinyxml2-10.0.0-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:26a1eb30c2a00bfc172e89ed015a18b8efb2b383546252ca8859574aed684686", size = 109487, upload-time = "2025-02-06T10:28:47.546Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/48/d8c81ce19b4b278ed0e8f81f93ae8670209bf3a9ac20141b9c386bb40cc7/cmeel_tinyxml2-10.0.0-0-py3-none-manylinux_2_17_i686.whl", hash = "sha256:53d86e02864c712f51f9a9adfcd8b6046b2ed51d44a0c34a8438d93b72b48325", size = 160118, upload-time = "2025-02-06T10:28:49.627Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4e/62193e27c9581f8ba7aeaeca7805632a64f2f4a824b1db37ad02ee953e8a/cmeel_tinyxml2-10.0.0-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:74112e2e9473afbf6ee2d25c9942553e9f6a40465e714533db72db48bc7658e1", size = 158477, upload-time = "2025-02-06T10:28:51.667Z" },
+ { url = "https://files.pythonhosted.org/packages/14/f9/d0420c39e9ade99beeec61cd3abc68880fe6e14d85e9df292af8fabe65c8/cmeel_tinyxml2-10.0.0-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:ecd6e99caa2a06ac0d4b333b740c20fca526d0ca426f99eb5c0a0039117afdb6", size = 147025, upload-time = "2025-02-06T10:28:53.944Z" },
+ { url = "https://files.pythonhosted.org/packages/66/9e/df63147fc162ab487217fa5596778ab7a81a82d9b3ce4236fd3a1e48cecb/cmeel_tinyxml2-10.0.0-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:30993fffb7032a45d5d3b1e5670cb879dad667a13144cd68c8f4e0371a8a3d2e", size = 150958, upload-time = "2025-02-06T10:28:55.301Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/a8/b03567275fd83f5af33ddb61de942689dec72c5b21bec01e6a5b11101aa5/cmeel_tinyxml2-10.0.0-0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8c09ede51784af54211a6225884dc7ddbb02ea1681656d173060c7ad2a5b9a3c", size = 160300, upload-time = "2025-02-06T10:28:57.189Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/ec/2781635b66c1059ca1243ae0f5a0410e171a5d8b8a71be3e34cb172f9f2d/cmeel_tinyxml2-10.0.0-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3bd511d6d0758224efdebc23d3ead6e94f0755b04141ebf7d5493377829e8332", size = 149184, upload-time = "2025-02-06T10:28:58.734Z" },
+]
+
+[[package]]
+name = "cmeel-urdfdom"
+version = "4.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-console-bridge" },
+ { name = "cmeel-tinyxml2" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/31/09/be81a5e7db56f34b6ccdbe7afe855c95a18c8439e173519e0146e9276a8c/cmeel_urdfdom-4.0.1.tar.gz", hash = "sha256:2e3f41e8483889e195b574acb326a4464cf11a3c0a8724031ac28bcda2223efc", size = 291511, upload-time = "2025-02-12T12:07:09.699Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/be/d0/20147dd6bb723afc44a58d89ea624df2bad1bed7b898a2df112aaca4a479/cmeel_urdfdom-4.0.1-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:2fe56939c6b47f6ec57021aac154123da47ecdcd79a217f3a5e3c4b705a07dee", size = 300860, upload-time = "2025-02-12T12:06:58.536Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/98/f832bca347e2d987c6b0ebb6930caf7b2c402535324aeed466b6aa2c4513/cmeel_urdfdom-4.0.1-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:00a0aba78b68c428b27abeed1db58d73e65319ed966911a0e97b37367442e756", size = 300616, upload-time = "2025-02-12T12:07:00.556Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/10/bf5765b6f388037cff166a754a0958ac2fee34ca3c0975ef64d0324e4647/cmeel_urdfdom-4.0.1-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:a701a8f9671331f11b18ecf37a6537db546a21e6a0e5d0ff53341fea0693ed7f", size = 385951, upload-time = "2025-02-12T12:07:02.556Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/82/cb3f8f587d293a17bdbea15b50cdaa4a1e28e04583eb4cb4821685b89466/cmeel_urdfdom-4.0.1-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:12e39fc388c077d79fc9b3841d3d972a1da90b90de754d3363194c1540e18abf", size = 399619, upload-time = "2025-02-12T12:07:04.388Z" },
+ { url = "https://files.pythonhosted.org/packages/24/77/322d7ac92c692d8dfaeda9de2d937087d15e2b564dc457d656e5fde3991d/cmeel_urdfdom-4.0.1-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c4a83925df1d5923c4485c3eb2b80b3a61b14f119ab724fb5bd04cec494690ee", size = 373969, upload-time = "2025-02-12T12:07:06.222Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/63/bdc6b55cc8bd99bb9dce6be801b30feffaa1c3841ecb7f4fe4d137424518/cmeel_urdfdom-4.0.1-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4c4f44270971b3d05c45a4e21b1fb2df7e05a750363ae918f59532bff0bfe0e1", size = 388237, upload-time = "2025-02-12T12:07:08.326Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/2d/8463fc23230612daf4da1e31d3229f47708381f3ae4d1500f0f007ac0f92/cmeel_urdfdom-4.0.1-1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:f7535158f45992eb2ba79e90d9db1bf9adc3846d9c7ed3e7a8c1c4d5343afa37", size = 301006, upload-time = "2025-02-13T11:42:08.8Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/d5/c8cdf500e49300d85624cbc3ef804107ddcdc9c541b1d3f726bfb58a9fc1/cmeel_urdfdom-4.0.1-1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fef2a01a00d61d41b3d35dd4958bba973e9025c26eea1d3c9880932f4dba89a5", size = 300758, upload-time = "2025-02-13T11:42:10.449Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/b3/2f7bac1544113a7f8e0f6d8b1fab5e75c6a3d27ffbb584b03267251b2165/cmeel_urdfdom-4.0.1-1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:7a52eb36950ce982014d99a55717ca29985da056e3705f20746f15d3244c1f7a", size = 386043, upload-time = "2025-02-13T11:42:11.923Z" },
+ { url = "https://files.pythonhosted.org/packages/86/03/8bdeb36ba6a3e8125d523ecfc010403049e463fe589f9896858d4bdcaf1e/cmeel_urdfdom-4.0.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:9f3b9c80b10d7246821ff61c2573f799e3da23d483e6f7367ddcad8a48baf58f", size = 399719, upload-time = "2025-02-13T11:42:14.325Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ed/43f99e7512460294cd8acc5753ba25f8a20bdf28d62e143eaf3ec7a28bb6/cmeel_urdfdom-4.0.1-1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2de69f47e8312cc09157624802d5bdaad6406443f863fb4b9ec62a19b4de3c72", size = 374073, upload-time = "2025-02-13T11:42:17.907Z" },
+ { url = "https://files.pythonhosted.org/packages/17/c6/2e9bde6d7c02c1cf203ea896f8ce1afd441412f09b44830f1ee4a96d77de/cmeel_urdfdom-4.0.1-1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7708c1402de450fbeab21f7ca264a9a4676ed4c1cdf8d84d840bc5d057aac920", size = 388337, upload-time = "2025-02-13T11:42:19.657Z" },
+]
+
+[[package]]
+name = "cmeel-zlib"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/74/b458f2fbfb652479c06400937cd67022e50d312033221602a9eca75022bc/cmeel_zlib-1.3.1.tar.gz", hash = "sha256:ebb34c54d1b7921dee5e7cd7003c9203b3297a5ba9d93983f1b7d3bb04976c3a", size = 3051, upload-time = "2025-02-11T12:20:39.574Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/dd/1bc2bc50c4ea217a993b2c9d3a7dd5959f839bc2b941556326b1ce71b961/cmeel_zlib-1.3.1-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:810779922c64d8074a3d12fcc471b1f62255e4402a1ca5f91f5749cc89214b93", size = 268796, upload-time = "2025-02-11T12:20:26.953Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/94/cf7e4554b7e2e4348da3f456be3c495774d1972a8dba384b6558b8f0e66b/cmeel_zlib-1.3.1-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2ccfac8fc80c6ee94ac61a9991f2ac18a5ea3a6cc2e753c221eb7c82729e839d", size = 191024, upload-time = "2025-02-11T12:20:28.737Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/cf/92d5a06071326ce3208f6cabc6d07d6c285b415df67e7ea9b87f0b46d44b/cmeel_zlib-1.3.1-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f59862cde12d0dcd51fc8f35c408a51e0f279f9d8d9103d5497fe82572e194e4", size = 286338, upload-time = "2025-02-11T12:20:30.784Z" },
+ { url = "https://files.pythonhosted.org/packages/21/10/13b53ce0f693085cbad31be9fceb1b6a2b4e3bae5851c1f114c3e7b3c447/cmeel_zlib-1.3.1-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7f95b4ed5090fb0fef195f52485f3719dd60213e67a4c07ac4718660bd24da25", size = 282556, upload-time = "2025-02-11T12:20:32.337Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/2e/58b295975403b147e5df681e3e3470ba1802feed06a836843f02386d6506/cmeel_zlib-1.3.1-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2864a55ab1dad1d86749c8410693f3bca6e866cbb5ac16286be686aedb781f6e", size = 287625, upload-time = "2025-02-11T12:20:34.471Z" },
+ { url = "https://files.pythonhosted.org/packages/56/f3/4da9d5c5308ef2019ab65a8a9f519ac95004446902d01e859f9ac6b8cdd6/cmeel_zlib-1.3.1-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e36ac8dccca22ff1f6e4df428ae5597f6288d9e6f85b08c9b767dc63e90fb55", size = 285662, upload-time = "2025-02-11T12:20:37.298Z" },
+]
+
+[[package]]
+name = "coal-library"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-assimp" },
+ { name = "cmeel-boost" },
+ { name = "cmeel-octomap" },
+ { name = "cmeel-qhull" },
+ { name = "eigenpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d0/20/ad159b6d9eb7f4158c7dfa71e335cd89c35288de1ae23be06e24a986a6d0/coal_library-3.0.1.tar.gz", hash = "sha256:40aa6d4e7d6dbe259d3637e697380c8fdec0dcec89886e9cf0cbd36d2a9df2e5", size = 3349502, upload-time = "2025-02-12T19:25:48.349Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/ee/14464beb631396090028267fe4cd167a38ee382bb6f7184961eeb2ab88e7/coal_library-3.0.1-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9d44f1311f165603eab3f4abc96aed9f5739310305638937fbe6dd5cc563be", size = 4530582, upload-time = "2025-02-12T19:24:38.93Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/e3/07c5e05cf2d0f2feef5b9e226f30d11cbca29f5c30173ec8a3fc93f01e59/coal_library-3.0.1-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e1bdc7575afcdda27a7487af19b7ffc42272fa1859d56f74b40b9512fb23931", size = 4123196, upload-time = "2025-02-12T19:24:41.966Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/49/0529dddc58fe7d435d43e50b33852ec5f8121f57e87cce1d7bfce4929a23/coal_library-3.0.1-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:566a61a913b037e9e6118a42cc1a1170c4b126dec4f13c5628d523e6a93a473a", size = 6388701, upload-time = "2025-02-12T19:24:44.421Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/0c/165082211ab7df98ab1aacae6f7354f1b3e6c67cc966ec8ef6ea27c8e229/coal_library-3.0.1-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c939380c0dc8a3cbb177ddd14c5ddac5f8beeb3d6b8b1e89d4a7d533567532b3", size = 6437127, upload-time = "2025-02-12T19:24:47.545Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/fe/8511aba274acbc724678c9b7944cca5c298e038b4c1316e501a4cfca2b0c/coal_library-3.0.1-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e26cfc2c3953d263cf4c9ba873794e0b79736a5d391fdfababcc161929bf5ed7", size = 4530584, upload-time = "2025-02-12T19:24:53.524Z" },
+ { url = "https://files.pythonhosted.org/packages/01/98/8090cc15e4733b14de32f54523f1c2f007b826cbdb0b9a2aa864186cb0b8/coal_library-3.0.1-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a7ba0b7f51d1e419466b5b1778730547bb8b240fb3f5e373dd9ec9ec372ecb0", size = 4123201, upload-time = "2025-02-12T19:24:55.669Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/38/62d5effedab4ad9312e38b91e462b15bca42fbb6a02f2400ef67dbb618eb/coal_library-3.0.1-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:64129fe9fa2374134a8d18b83051da229ff9987773df7cf0d2a8bd1dbbe6139e", size = 6388488, upload-time = "2025-02-12T19:25:00.058Z" },
+ { url = "https://files.pythonhosted.org/packages/68/40/4ae9c8f09532174c49bd152c46efc71f4ee5596236eaef9901ba643b33d5/coal_library-3.0.1-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a00492012bb43d3386041872cf98d7fdd7cc546a6bb53cba4a478de8c70a0fa4", size = 6436691, upload-time = "2025-02-12T19:25:02.124Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/29/469f0b0b086576413e92f68b83bec3ca29b2e0f17c27f2f7c24cd0700d32/coal_library-3.0.1-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7f0c01e839c455712b540e15d8719ff986e726beed306c7f73424c37021e705", size = 4549472, upload-time = "2025-02-12T19:25:06.779Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/d6/d6b2f7256775ec71886d3ffbc3d00bed3594bef23314adaa6e3c23853e5d/coal_library-3.0.1-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1def214c7aaee3e45dbcdc9049c28096cb945b194b5eb5cad867d5811430255e", size = 4129424, upload-time = "2025-02-12T19:25:08.584Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/b5/3014b8f9382539f72ab30f32cd263ee4c885b9926180f79ed7d7853f4a8b/coal_library-3.0.1-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ad7e8ecd9421b02190a2d4f504d8c53a63f91ab14182deadb508a7e44efba367", size = 6360948, upload-time = "2025-02-12T19:25:12.518Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/4c/b8c6c3627f1fe0be7972e38fb50899af8de87745dc8ceb40af82badf185e/coal_library-3.0.1-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:5701232b34d2c70b622066c8796079ecda9bac77a60044650daed25da77637ef", size = 6421379, upload-time = "2025-02-12T19:25:14.593Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/c2/b45fb444004d4026d936297b2c5183fca7c42ad31b1399020ce6c57ba84f/coal_library-3.0.1-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:8594b26e115d1a78b13db4dbcd2b25e834517734f104decd355d09e3b494d191", size = 4549475, upload-time = "2025-02-12T19:25:17.645Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/a9/1c6c3a9d480b8b97c3eafc3c964454f4046d2ea6b780dc0dbc801f9bd41f/coal_library-3.0.1-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61a33e9e6d2bd695da718468d0cd3d9b6e20d4dafced90a38b1c17399b3ab73d", size = 4129423, upload-time = "2025-02-12T19:25:21.518Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2a/ba0c897358597b6976aee28e5e849f87ddae132cce04d9e8415003473a0a/coal_library-3.0.1-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:1013d326633664f3701f91007b9738d8d79465da5a15cd75c65e696304fff366", size = 6360950, upload-time = "2025-02-12T19:25:25.579Z" },
+ { url = "https://files.pythonhosted.org/packages/10/f4/615449e99cb23a258a9a9178ea7b56e634b3b1e4b4f07dce79708d3ad245/coal_library-3.0.1-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:96ce11ffe98215ce6845c435322a1e55297c97f532e0e22ef41e815887d09a41", size = 6421379, upload-time = "2025-02-12T19:25:27.936Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "coloredlogs"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "humanfriendly" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" },
+]
+
+[[package]]
+name = "colorzero"
+version = "2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/688824a06e8c4d04c7d2fd2af2d8da27bed51af20ee5f094154e1d680334/colorzero-2.0.tar.gz", hash = "sha256:e7d5a5c26cd0dc37b164ebefc609f388de24f8593b659191e12d85f8f9d5eb58", size = 25382, upload-time = "2021-03-15T23:42:23.261Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/a6/ddd0f130e44a7593ac6c55aa93f6e256d2270fd88e9d1b64ab7f22ab8fde/colorzero-2.0-py2.py3-none-any.whl", hash = "sha256:0e60d743a6b8071498a56465f7719c96a5e92928f858bab1be2a0d606c9aa0f8", size = 26573, upload-time = "2021-03-15T23:42:21.757Z" },
+]
+
+[[package]]
+name = "commentjson"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "lark-parser" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/76/c4aa9e408dbacee3f4de8e6c5417e5f55de7e62fb5a50300e1233a2c9cb5/commentjson-0.9.0.tar.gz", hash = "sha256:42f9f231d97d93aff3286a4dc0de39bfd91ae823d1d9eba9fa901fe0c7113dd4", size = 8653, upload-time = "2020-10-05T18:49:06.524Z" }
+
+[[package]]
+name = "cv2-enumerate-cameras"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/67/e9/1ed371073498f6a4be7cf32dd19605e7fa067497328668a1083e85a3ae71/cv2_enumerate_cameras-1.2.2.tar.gz", hash = "sha256:7dce1335605a4d19fc402c2b3b7d45d5bd80143c20b77be3655f96f30ad76510", size = 11942, upload-time = "2025-07-03T06:05:00.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/37/a198560e077b82092f8d7df438e5c27fc5cc97a721456368f248c6cbf62f/cv2_enumerate_cameras-1.2.2-cp32-abi3-win32.whl", hash = "sha256:5068197f58ec5803da84dd9caa23dd016e09d0c2e6a656d888c5c29abb4ec226", size = 19866, upload-time = "2025-07-03T06:04:56.418Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ad/efd8779737ccb88596f9a06f4c46389f4c72aca3696b4b8260d18687d8d6/cv2_enumerate_cameras-1.2.2-cp32-abi3-win_amd64.whl", hash = "sha256:b5c2a765161a4187c5af3dbf2dd4fdf5acd486afde0bda2375eed877852117e5", size = 20751, upload-time = "2025-07-03T06:04:57.513Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d5/be643f4c9542fa07752874820daff2a825c9c67432dfab6853662384385f/cv2_enumerate_cameras-1.2.2-py3-none-any.whl", hash = "sha256:0e54301a65a44a69873f4b4300c37119ae0ed1ea0266d96e1504fcbad678a8d5", size = 11024, upload-time = "2025-07-03T06:04:58.862Z" },
+]
+
+[[package]]
+name = "decorator"
+version = "5.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
+]
+
+[[package]]
+name = "distlib"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
+]
+
+[[package]]
+name = "docutils"
+version = "0.22.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d9/02/111134bfeb6e6c7ac4c74594e39a59f6c0195dc4846afbeac3cba60f1927/docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd", size = 2290153, upload-time = "2025-11-06T02:35:55.655Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb", size = 633032, upload-time = "2025-11-06T02:35:52.391Z" },
+]
+
+[[package]]
+name = "eclipse-zenoh"
+version = "1.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/59/46/501fa9a336c12a0c6f59a296233e24c00e7f30189d75cb692cab90e0b39f/eclipse_zenoh-1.5.1.tar.gz", hash = "sha256:5525adfe368e548e9a80947dbb930eedb4ae3df139bafc70358306483949b591", size = 126895, upload-time = "2025-09-04T13:53:02.988Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/63/d3beeb3bc882562bb6ea0a4caecd84a71d88662625bf20949b92371b2046/eclipse_zenoh-1.5.1-cp38-abi3-linux_armv6l.whl", hash = "sha256:c1d494012ef3c693a92980129857a73a4c3f3f0e2af0d0c0274c3615cd48ffda", size = 9084619, upload-time = "2025-09-04T13:52:46.991Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/76/05de0c17972dc40adf083a23ea05b094a7ed238cb9f01af926d4f1947b99/eclipse_zenoh-1.5.1-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7f124d00868a03640afaa63fdddbf9befc2ea385c83e8944ea62e029363941c4", size = 16904800, upload-time = "2025-09-04T13:52:49.111Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/40/6106308e28595cf78bbc3d91fcc389cf6def17b36e5eded29c1db0945e60/eclipse_zenoh-1.5.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:5dcaa3a16dd0e8d517c7879f4b1dd6147ba48a15ffd12cc41ecb9b89e618f50b", size = 8652691, upload-time = "2025-09-04T13:52:51.382Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/be/2128b27738576d9e80452690682170922cf576e782db8226d9309705bdf2/eclipse_zenoh-1.5.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8e9f2fc126fce777e12842c4cb62235e854fa6df73afea71c04a28d7471e0b0a", size = 8974218, upload-time = "2025-09-04T13:52:52.964Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/fa/4eda37827209c7856d06e6df86f48b53c77291b158baaef4702c90fc3fe7/eclipse_zenoh-1.5.1-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21239d912c758a0fe3dd8a76454d48f493b169de7795f050cf8045c5d8ea335f", size = 9771132, upload-time = "2025-09-04T13:52:55.008Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4f/a9150d237f6ef71489395127d9c5ddd77fe5fb64864b92e0746eb04dc8a8/eclipse_zenoh-1.5.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76781bbfa61fdc1e9626a03202c7dd8fd103f34761dc7b94b5a2d99f5aae629e", size = 9264787, upload-time = "2025-09-04T13:52:56.761Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/1d/9309130f890c6eb783703b5aa2c4718846900f810d123a1228c45416d871/eclipse_zenoh-1.5.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f142a36241e50eb3b86f9c3b0149138d2d1d7b6dd2038589fe4c62e12e776ea", size = 9089753, upload-time = "2025-09-04T13:52:59.851Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/8a/bec6c2e6fe808afab7e3c7c75eba3e3fa7e549d5028b1bdd7f983cd6d2eb/eclipse_zenoh-1.5.1-cp38-abi3-win_amd64.whl", hash = "sha256:eab374670fa9054e2500060ce33c8248f079208476defa5bb9f9a4b8317d6aac", size = 7581657, upload-time = "2025-09-04T13:53:01.467Z" },
+]
+
+[[package]]
+name = "eigenpy"
+version = "3.10.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-boost" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/39/d3/ffe0d1b75223013420bf8645aa06e29745366e6f8777e88d78947ef47846/eigenpy-3.10.3.tar.gz", hash = "sha256:95fc73d29ee9d175d8a35484f1cdc79349e54f9b3da5a1a695889c37b0c5d2e4", size = 6529791, upload-time = "2025-02-12T00:19:52.366Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/91/c13f031e420854dfb9d33185423fa7d8ab81bd55340dcfc745182fe0e51e/eigenpy-3.10.3-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9f8d960640f5c498bbb05ab07d23fd544cc198c18cbc4af624eb17009543fabb", size = 4912581, upload-time = "2025-02-12T00:18:55.012Z" },
+ { url = "https://files.pythonhosted.org/packages/82/91/5b74cd05f204f5c63734d0ddf27f3ddf2c7468911c527fe99006f17f55d2/eigenpy-3.10.3-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6f64b81fbb53a06fca5538b47cbef9b5f6cc7de239e4e8b85456938db568a9b", size = 4256333, upload-time = "2025-02-12T00:18:59.314Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/52/b5276d9ce6785595200b442a797c718110d1c5e65a1ed7287b56e5a011d1/eigenpy-3.10.3-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c585788efef343beb797e4ec2a09e7145f2324aa1bf51847e5729ad6d182bc89", size = 5205940, upload-time = "2025-02-12T00:19:01.838Z" },
+ { url = "https://files.pythonhosted.org/packages/48/47/69fc45199bc759b0f686098ddc8a983b6b5784b1cdaa6b2cdab8883d187b/eigenpy-3.10.3-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:af04ee75774b5e7b3fde4203e15d3fd23de178d70f7a3bdff6fba5a5395ad64a", size = 4939339, upload-time = "2025-02-12T00:19:06.264Z" },
+ { url = "https://files.pythonhosted.org/packages/57/b1/08e4a09011cae328c3b00a427b9834b48c75a88bfe34c84d371e1d5870fa/eigenpy-3.10.3-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d1967cdff87dd49828a41ce0336dfaeb5efb26103d047ddafa35a42dc3cd889a", size = 4912589, upload-time = "2025-02-12T00:19:08.911Z" },
+ { url = "https://files.pythonhosted.org/packages/28/3c/6ab509f3765a38f62205505694bba5ed018e1c37047a9d82a32d0b582ae7/eigenpy-3.10.3-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:12a174ab89aa7608ff33f7ce139846d24250e9402a32c257b917dce8dac082c8", size = 4256296, upload-time = "2025-02-12T00:19:10.633Z" },
+ { url = "https://files.pythonhosted.org/packages/02/8a/4a572050d33d959ff8fd9ad9e4f9fbf9c373cdf5b0b25369fc1fe8afce8a/eigenpy-3.10.3-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b044ec5c1320fa6863b717e302db45220088c3467483fead92ed6effea573982", size = 5205135, upload-time = "2025-02-12T00:19:13.337Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/06/f3ddc152d7f453b309547118ff0b92f6b3ab6d40b287240e5726f5e2b7ce/eigenpy-3.10.3-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2da3284867d224c067b04f784a1c35ff1350a540556076bffcf39d8b52f7615f", size = 4939523, upload-time = "2025-02-12T00:19:15.224Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/de/b96e78da9b94470f984a6dff57a59f09b14020517dd2ff8c85d4cae99f4d/eigenpy-3.10.3-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c758bd224df13a0e6176162b929aec8026beba01edf416ae34bf86eed6293f50", size = 4901896, upload-time = "2025-02-12T00:19:17.159Z" },
+ { url = "https://files.pythonhosted.org/packages/33/36/9a9dd442c6daf6e2a03803766b5ce9675f9015d4aac3dc0c0789b3e941f3/eigenpy-3.10.3-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a681e6151e98a67af2b1b30c61686097d3089203ac8627e32ebd68f75627baf", size = 4264524, upload-time = "2025-02-12T00:19:19.715Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/06/06b05cce5ba8502206ed0951a15e83f6c015e9a3a3d296dd47effef6c3f9/eigenpy-3.10.3-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5e00483d44edf961556536f7557dbd84106154cbe84cb1056249c7c2b719e67f", size = 5196136, upload-time = "2025-02-12T00:19:22.457Z" },
+ { url = "https://files.pythonhosted.org/packages/93/0c/3efa6ec843e530d07436513972c92c5dc0ed9e2f774ab265b782beb4eeab/eigenpy-3.10.3-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f151f6fe4f5b575429d69411e12881622b1e76f6fd3805b4081c397fe46cf229", size = 4944229, upload-time = "2025-02-12T00:19:25.356Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/25/3299d29624fdc2f8e218756e5823f699209b0a8710d3afecb3f33df2d560/eigenpy-3.10.3-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:c3874aaef6562d4e482f591b650ed48d6f5a81045a268d6498a4a3506b838893", size = 4901894, upload-time = "2025-02-12T00:19:27.735Z" },
+ { url = "https://files.pythonhosted.org/packages/25/34/2283ff0b753528b924b420889a38a37524ed540d8bde63f60fd935139f04/eigenpy-3.10.3-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e717de8660cee84412832d3f41c3a2769b0cb21f8471bacbde3b6b37cadeeb9", size = 4264518, upload-time = "2025-02-12T00:19:30.417Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d9/8e54eb7e6908a9445c304b4655503ca0b8eb258156c968ceeade3b527a85/eigenpy-3.10.3-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d047e63716138ddda51efc7c71c7f7c422489af8585f9f012bd0f31d74c443e2", size = 5196137, upload-time = "2025-02-12T00:19:33.215Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e7/38f1de666adb78de94d2a549bdf4db48d2c447d4d052a3c7e9755d7b71e5/eigenpy-3.10.3-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ece421651d974a702f99333dec4d2a5b47c2a67bb17913c288fe3bb4bcf99744", size = 4944218, upload-time = "2025-02-12T00:19:35.233Z" },
+]
+
+[[package]]
+name = "eiquadprog"
+version = "1.2.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-boost" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c1/14/3e7fa16567e08e43e6e58aaeba9299977dd0d1c4ac5415e3b9a4a366a64f/eiquadprog-1.2.9.tar.gz", hash = "sha256:7fc82c4c476f636ff1aea4fd3c0baf301b4a6d349a0f3333d1600913d228ca3c", size = 879999, upload-time = "2025-02-17T19:00:17.851Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a6/a0/a2bfde96870eb65029ce85a46a74a331e143454640f6d3266536d3d94997/eiquadprog-1.2.9-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:ffcdb57617894fbd6693aee309f8bc20acb5f810071091b38e22c6c92690dead", size = 102743, upload-time = "2025-02-17T19:00:05.699Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/87/63ef7a51df432f4f3a81de5f68c0655c866e86268862d62e6c11eb8c1a8c/eiquadprog-1.2.9-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b88425ca352a9480cca6991303551c9bff5607d3d2d2e31bb2293a06de266ff", size = 90721, upload-time = "2025-02-17T19:00:07.101Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/c9/21a8b3630749f418223e7c55e1479652ce3e482f4d070e675245172e01f0/eiquadprog-1.2.9-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:a45d8e59ffb7512d07ddea261a95beb4a7e13e7bc65b2df3dd5a49f611b79bdb", size = 106935, upload-time = "2025-02-17T19:00:08.931Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/cd/c80c605afde9c7f4a369721faea9f4bf61f1b9addcba6f6ad365a00d54cb/eiquadprog-1.2.9-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:439a1e80a60e09d4483f7937c36a3d114c8c8d0534cb53abb7a55bbea496bb03", size = 112873, upload-time = "2025-02-17T19:00:10.805Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/98/63beb8f79f68d15d057ced461573ba733e0404dcc4da5a0b170717433bc8/eiquadprog-1.2.9-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:95ac0a753ceb6d2e232273cfa6e7a3b868782c6e1d4e4632d8e6279eef64459c", size = 108609, upload-time = "2025-02-17T19:00:12.904Z" },
+ { url = "https://files.pythonhosted.org/packages/64/cb/809f0c3e4e7bfe78c6dd468631896a8866c3ba853e3c855cc3fa58fae660/eiquadprog-1.2.9-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:416f4b584ea30072f166b2a6a3e0a63a2a260a378f9bcbd2dfc9cde13b810a50", size = 118538, upload-time = "2025-02-17T19:00:16.297Z" },
+]
+
+[[package]]
+name = "etils"
+version = "1.13.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9b/a0/522bbff0f3cdd37968f90dd7f26c7aa801ed87f5ba335f156de7f2b88a48/etils-1.13.0.tar.gz", hash = "sha256:a5b60c71f95bcd2d43d4e9fb3dc3879120c1f60472bb5ce19f7a860b1d44f607", size = 106368, upload-time = "2025-07-15T10:29:10.563Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl", hash = "sha256:d9cd4f40fbe77ad6613b7348a18132cc511237b6c076dbb89105c0b520a4c6bb", size = 170603, upload-time = "2025-07-15T10:29:09.076Z" },
+]
+
+[package.optional-dependencies]
+epath = [
+ { name = "fsspec" },
+ { name = "importlib-resources" },
+ { name = "typing-extensions" },
+ { name = "zipp" },
+]
+
+[[package]]
+name = "evdev"
+version = "1.9.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz", hash = "sha256:5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069", size = 33301, upload-time = "2025-05-01T19:53:47.69Z" }
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
+]
+
+[[package]]
+name = "executing"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.116.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.19.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" },
+]
+
+[[package]]
+name = "flatbuffers"
+version = "25.2.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e", size = 22170, upload-time = "2025-02-11T04:26:46.257Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051", size = 30953, upload-time = "2025-02-11T04:26:44.484Z" },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" },
+ { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" },
+ { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" },
+ { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" },
+ { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" },
+ { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" },
+ { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" },
+ { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" },
+ { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" },
+ { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" },
+ { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" },
+ { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" },
+ { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" },
+ { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" },
+ { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" },
+ { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" },
+ { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" },
+ { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" },
+ { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" },
+ { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" },
+ { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" },
+ { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" },
+ { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" },
+ { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" },
+ { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" },
+ { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" },
+ { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" },
+ { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" },
+ { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" },
+ { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" },
+ { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" },
+ { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" },
+ { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" },
+ { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" },
+ { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" },
+ { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" },
+ { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" },
+ { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" },
+]
+
+[[package]]
+name = "fsspec"
+version = "2025.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" },
+]
+
+[[package]]
+name = "glfw"
+version = "2.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/97/a2d667c98b8474f6b8294042488c1bd488681fb3cb4c3b9cdac1a9114287/glfw-2.9.0.tar.gz", hash = "sha256:077111a150ff09bc302c5e4ae265a5eb6aeaff0c8b01f727f7fb34e3764bb8e2", size = 31453, upload-time = "2025-04-15T15:39:54.142Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/21/71/13dd8a8d547809543d21de9438a3a76a8728fc7966d01ad9fb54599aebf5/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl", hash = "sha256:183da99152f63469e9263146db2eb1b6cc4ee0c4082b280743e57bd1b0a3bd70", size = 105297, upload-time = "2025-04-15T15:39:39.677Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/a2/45e6dceec1e0a0ffa8dd3c0ecf1e11d74639a55186243129160c6434d456/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_11_0_arm64.whl", hash = "sha256:aef5b555673b9555216e4cd7bc0bdbbb9983f66c620a85ba7310cfcfda5cd38c", size = 102146, upload-time = "2025-04-15T15:39:42.354Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/72/b6261ed918e3747c6070fe80636c63a3c8f1c42ce122670315eeeada156f/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux2014_aarch64.whl", hash = "sha256:fcc430cb21984afba74945b7df38a5e1a02b36c0b4a2a2bab42b4a26d7cc51d6", size = 230002, upload-time = "2025-04-15T15:39:43.933Z" },
+ { url = "https://files.pythonhosted.org/packages/45/d6/7f95786332e8b798569b8e60db2ee081874cec2a62572b8ec55c309d85b7/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux2014_x86_64.whl", hash = "sha256:7f85b58546880466ac445fc564c5c831ca93c8a99795ab8eaf0a2d521af293d7", size = 241949, upload-time = "2025-04-15T15:39:45.28Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/e6/093ab7874a74bba351e754f6e7748c031bd7276702135da6cbcd00e1f3e2/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_aarch64.whl", hash = "sha256:2123716c8086b80b797e849a534fc6f21aebca300519e57c80618a65ca8135dc", size = 231016, upload-time = "2025-04-15T15:39:46.669Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ba/de3630757c7d7fc2086aaf3994926d6b869d31586e4d0c14f1666af31b93/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_x86_64.whl", hash = "sha256:4e11271e49eb9bc53431ade022e284d5a59abeace81fe3b178db1bf3ccc0c449", size = 243489, upload-time = "2025-04-15T15:39:48.321Z" },
+ { url = "https://files.pythonhosted.org/packages/32/36/c3bada8503681806231d1705ea1802bac8febf69e4186b9f0f0b9e2e4f7e/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win32.whl", hash = "sha256:8e4fbff88e4e953bb969b6813195d5de4641f886530cc8083897e56b00bf2c8e", size = 552655, upload-time = "2025-04-15T15:39:50.029Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/70/7f2f052ca20c3b69892818f2ee1fea53b037ea9145ff75b944ed1dc4ff82/glfw-2.9.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win_amd64.whl", hash = "sha256:9aa3ae51601601c53838315bd2a03efb1e6bebecd072b2f64ddbd0b2556d511a", size = 559441, upload-time = "2025-04-15T15:39:52.531Z" },
+]
+
+[[package]]
+name = "gpiozero"
+version = "2.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorzero" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e4/47/334b8db8a981eca9a0fb1e7e48e1997a5eaa8f40bb31c504299dcca0e6ff/gpiozero-2.0.1.tar.gz", hash = "sha256:d4ea1952689ec7e331f9d4ebc9adb15f1d01c2c9dcfabb72e752c9869ab7e97e", size = 136176, upload-time = "2024-02-15T11:07:02.919Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/eb/6518a1b00488d48995034226846653c382d676cf5f04be62b3c3fae2c6a1/gpiozero-2.0.1-py3-none-any.whl", hash = "sha256:8f621de357171d574c0b7ea0e358cb66e560818a47b0eeedf41ce1cdbd20c70b", size = 150818, upload-time = "2024-02-15T11:07:00.451Z" },
+]
+
+[[package]]
+name = "gst-signalling"
+version = "1.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "pyee" },
+ { name = "pygobject" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3f/77/0c45062d07bb6ad8f940a8a8bc3acd7968cc145be354a566e897d4a6753c/gst_signalling-1.1.2.tar.gz", hash = "sha256:45c7bb3d6801b2704cd4c743130f60d2991b39aa91b6220873ef53ffe9910b98", size = 16467, upload-time = "2025-11-18T13:36:35.935Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/84/7a/4ac380ce2e7e9ea1e055460648d6c562eb554cdc0dd9b6536b38934b9ef4/gst_signalling-1.1.2-py3-none-any.whl", hash = "sha256:39ebb6eac597933504334d4c1f835abd873cbd42fe31896515e3a0db368db78c", size = 19681, upload-time = "2025-11-18T13:36:34.567Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "hf-xet"
+version = "1.1.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/23/0f/5b60fc28ee7f8cc17a5114a584fd6b86e11c3e0a6e142a7f97a161e9640a/hf_xet-1.1.9.tar.gz", hash = "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803", size = 484242, upload-time = "2025-08-27T23:05:19.441Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/12/56e1abb9a44cdef59a411fe8a8673313195711b5ecce27880eb9c8fa90bd/hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160", size = 2762553, upload-time = "2025-08-27T23:05:15.153Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/e6/2d0d16890c5f21b862f5df3146519c182e7f0ae49b4b4bf2bd8a40d0b05e/hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a", size = 2623216, upload-time = "2025-08-27T23:05:13.778Z" },
+ { url = "https://files.pythonhosted.org/packages/81/42/7e6955cf0621e87491a1fb8cad755d5c2517803cea174229b0ec00ff0166/hf_xet-1.1.9-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c", size = 3186789, upload-time = "2025-08-27T23:05:12.368Z" },
+ { url = "https://files.pythonhosted.org/packages/df/8b/759233bce05457f5f7ec062d63bbfd2d0c740b816279eaaa54be92aa452a/hf_xet-1.1.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790", size = 3088747, upload-time = "2025-08-27T23:05:10.439Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/3c/28cc4db153a7601a996985bcb564f7b8f5b9e1a706c7537aad4b4809f358/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95", size = 3251429, upload-time = "2025-08-27T23:05:16.471Z" },
+ { url = "https://files.pythonhosted.org/packages/84/17/7caf27a1d101bfcb05be85850d4aa0a265b2e1acc2d4d52a48026ef1d299/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea", size = 3354643, upload-time = "2025-08-27T23:05:17.828Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/50/0c39c9eed3411deadcc98749a6699d871b822473f55fe472fad7c01ec588/hf_xet-1.1.9-cp37-abi3-win_amd64.whl", hash = "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127", size = 2804797, upload-time = "2025-08-27T23:05:20.77Z" },
+]
+
+[[package]]
+name = "httptools"
+version = "0.6.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload-time = "2024-10-16T19:44:06.882Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297, upload-time = "2024-10-16T19:44:08.129Z" },
+ { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130, upload-time = "2024-10-16T19:44:09.45Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148, upload-time = "2024-10-16T19:44:11.539Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949, upload-time = "2024-10-16T19:44:13.388Z" },
+ { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591, upload-time = "2024-10-16T19:44:15.258Z" },
+ { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344, upload-time = "2024-10-16T19:44:16.54Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload-time = "2024-10-16T19:44:18.427Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload-time = "2024-10-16T19:44:19.515Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload-time = "2024-10-16T19:44:21.067Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload-time = "2024-10-16T19:44:22.958Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload-time = "2024-10-16T19:44:24.513Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload-time = "2024-10-16T19:44:26.295Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload-time = "2024-10-16T19:44:29.188Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" },
+ { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" },
+ { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" },
+ { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" },
+ { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" },
+ { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.34.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" },
+]
+
+[[package]]
+name = "humanfriendly"
+version = "10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyreadline3", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/c4/62963f25a678f6a050fb0505a65e9e726996171e6dbe1547f79619eefb15/identify-2.6.14.tar.gz", hash = "sha256:663494103b4f717cb26921c52f8751363dc89db64364cd836a9bf1535f53cd6a", size = 99283, upload-time = "2025-09-06T19:30:52.938Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/ae/2ad30f4652712c82f1c23423d79136fbce338932ad166d70c1efb86a5998/identify-2.6.14-py2.py3-none-any.whl", hash = "sha256:11a073da82212c6646b1f39bb20d4483bfb9543bd5566fec60053c4bb309bf2e", size = 99172, upload-time = "2025-09-06T19:30:51.759Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
+]
+
+[[package]]
+name = "ipython"
+version = "8.37.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+dependencies = [
+ { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" },
+ { name = "decorator", marker = "python_full_version < '3.11'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "jedi", marker = "python_full_version < '3.11'" },
+ { name = "matplotlib-inline", marker = "python_full_version < '3.11'" },
+ { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
+ { name = "prompt-toolkit", marker = "python_full_version < '3.11'" },
+ { name = "pygments", marker = "python_full_version < '3.11'" },
+ { name = "stack-data", marker = "python_full_version < '3.11'" },
+ { name = "traitlets", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" },
+]
+
+[[package]]
+name = "ipython"
+version = "9.5.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13'",
+ "python_full_version >= '3.11' and python_full_version < '3.13'",
+]
+dependencies = [
+ { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" },
+ { name = "decorator", marker = "python_full_version >= '3.11'" },
+ { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" },
+ { name = "jedi", marker = "python_full_version >= '3.11'" },
+ { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" },
+ { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
+ { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" },
+ { name = "pygments", marker = "python_full_version >= '3.11'" },
+ { name = "stack-data", marker = "python_full_version >= '3.11'" },
+ { name = "traitlets", marker = "python_full_version >= '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version == '3.11.*'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" },
+]
+
+[[package]]
+name = "ipython-pygments-lexers"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pygments", marker = "python_full_version >= '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
+]
+
+[[package]]
+name = "ischedule"
+version = "1.2.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/78/46/0d471c8318050c182badc6b6c3d4d05c1705508de57175e6e316de76e43c/ischedule-1.2.7.tar.gz", hash = "sha256:517ac2c717c8de7e39ded69a86ab974348e5273e466a7ad2af6a096668ff9ba5", size = 7421, upload-time = "2024-03-15T19:13:12.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/0c/937caa3558fa5787927585cce67af4446fec8996d4c28c88d7efdbad6b14/ischedule-1.2.7-py3-none-any.whl", hash = "sha256:2a327e57e3ea179d3d9675cef1e7a0719f2a936c61a9d1ae288c3a5f5156939d", size = 5273, upload-time = "2024-03-15T19:13:09.105Z" },
+]
+
+[[package]]
+name = "jedi"
+version = "0.19.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "parso" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "lark-parser"
+version = "0.7.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/34/b8/aa7d6cf2d5efdd2fcd85cf39b33584fe12a0f7086ed451176ceb7fb510eb/lark-parser-0.7.8.tar.gz", hash = "sha256:26215ebb157e6fb2ee74319aa4445b9f3b7e456e26be215ce19fdaaa901c20a4", size = 276204, upload-time = "2019-11-01T12:45:26.558Z" }
+
+[[package]]
+name = "lgpio"
+version = "0.2.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/56/33/26ec2e8049eaa2f077bf23a12dc61ca559fbfa7bea0516bf263d657ae275/lgpio-0.2.2.0.tar.gz", hash = "sha256:11372e653b200f76a0b3ef8a23a0735c85ec678a9f8550b9893151ed0f863fff", size = 90087, upload-time = "2024-03-29T21:59:55.901Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/b9/d23f9539bbddf47c01a14fc96158a42d9de454fd9d04f7be1347ca6db8fd/lgpio-0.2.2.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:97fe5fb0e888c96031e8899e8c0eca64c63076a6d1f1e774acad8696430b2ff6", size = 376674, upload-time = "2024-03-29T22:00:41.969Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/db/fbbade15dbc9febdbd06bd82531c0a78206f96262003145d6953396d9554/lgpio-0.2.2.0-cp310-cp310-manylinux_2_34_aarch64.whl", hash = "sha256:f8f1a2818ed4293182999679ac8559aea70d45743f5a3ae8025837e529d9e0d4", size = 356711, upload-time = "2024-04-01T22:49:43.455Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ca/1c5278b2548e956a52a07efae91ce2300f81c8cf4ad3d7d6b98ce8987e15/lgpio-0.2.2.0-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:d245f315e4bc5ba1b72df9fd935a16c99e56ccf6b41d9f66a87804c1dfd91c86", size = 362126, upload-time = "2024-04-13T14:08:11.879Z" },
+ { url = "https://files.pythonhosted.org/packages/78/4e/5721ae44b29e4fe9175f68c881694e3713066590739a7c87f8cee2835c25/lgpio-0.2.2.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:5b3c403e1fba9c17d178f1bde102726c548fc5c4fc1ccf5ec3e18f3c08e07e04", size = 382992, upload-time = "2024-03-29T22:00:45.039Z" },
+ { url = "https://files.pythonhosted.org/packages/88/53/e57a22fe815fc68d0991655c1105b8ed872a68491d32e4e0e7d10ffb5c4d/lgpio-0.2.2.0-cp311-cp311-manylinux_2_34_aarch64.whl", hash = "sha256:a2f71fb95b149d8ac82c7c6bae70f054f6dc42a006ad35c90c7d8e54921fbcf4", size = 364848, upload-time = "2024-04-01T22:49:45.889Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/71/11f4e3d76400e4ca43f9f9b014f5a86d9a265340c0bea45cce037277eb34/lgpio-0.2.2.0-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:e9f4f3915abe5ae0ffdb4b96f485076d80a663876d839e2d3fd9218a71b9873e", size = 370183, upload-time = "2024-04-13T14:08:14.139Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/73/e56c9afb845df53492d42bdea01df9895272bccfdd5128f34719c3a07990/lgpio-0.2.2.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:6c65ac42e878764d04a71ed12fe6d46089b36e9e8127722bf29bb2e4bc91de22", size = 383956, upload-time = "2024-03-29T22:00:47.315Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/1c/becd00f66d2c65feed9a668ff9d91732394cb6baba7bec505d55de0e30c9/lgpio-0.2.2.0-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:d907db79292c721c605af08187385ddb3b7af09907e1ffca56cf0cd6558ace0a", size = 366058, upload-time = "2024-04-01T22:49:47.615Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/7a/e3b4e5225c9792c4092b2cc07504746acbe62d0a8e4cb023bdf65f6430cf/lgpio-0.2.2.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:2aadff092f642fcdada8457c158f87259dfda3a89ec19bae0b99ff22b34aac4b", size = 372103, upload-time = "2024-04-13T14:08:16.351Z" },
+]
+
+[[package]]
+name = "libusb-package"
+version = "1.0.26.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "importlib-resources" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/27/c989cd606683102170c2dc2e89771a22dc71b9d88c4d20c3a97f6d23a0a1/libusb_package-1.0.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ffa01d1db3ef9e7faa62b03f409cd077232885ae3fab6f95912db78035a41db", size = 63863, upload-time = "2025-04-01T12:59:12.09Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/e4/663a37d23b3a47a641f74556bb42c04b26c46b95fb8a65c11421cb0ccb0d/libusb_package-1.0.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:95bbf9872674318a23450cd92053eee01683eeae6b6aa76eba30ee5f37c3765b", size = 59502, upload-time = "2025-04-01T12:59:13.489Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/93/c99ea3b13539c501c41e605645693346e08cfcb7747025ee640502f7460d/libusb_package-1.0.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36a1c48779c8763fc6bc0331fda668c93b58d55934236d0393d3ec026875f7cd", size = 70247, upload-time = "2025-04-01T14:53:00.28Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/c4/84c7af13453e840ba2e3e9f247c9855035077a7214c1f0f273e1df5a845f/libusb_package-1.0.26.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:209efb9a78ac652afc2332b0a63ef2e423202fa3a1bebe5fe3c499e0922afc03", size = 74537, upload-time = "2025-04-01T14:53:01.995Z" },
+ { url = "https://files.pythonhosted.org/packages/79/f8/933f70e9a1e05e0d25fb7fb6a5a4512ba7845203b11afc163cfdc98e9b88/libusb_package-1.0.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433e89dd1f9f9a4149b975247cf1d493170454945fec54b4db9fe61c9e6b861f", size = 70652, upload-time = "2025-04-01T14:53:03.346Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/96/77f873c2a3a84b93439a973c70ecc53d2b9ae14cb45b4ba710b89d822228/libusb_package-1.0.26.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7e6fcad72db04b30c8495ac0df6a9b1a4ec8705930bfa2160cc9b018f14101a1", size = 71861, upload-time = "2025-04-01T14:53:04.507Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/6e/64f83de4274e4a10ad28a03ff7879d3765c5d7efe0e5c833938318a7de20/libusb_package-1.0.26.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:65ee502c4999ded1c71e38769b0a89152c1e03e43b0d35919f3e32a8cbc7cd99", size = 76476, upload-time = "2025-04-01T14:53:05.511Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/56/3792e6a41776d85a4113e75c256879355261b5dd1ed22eb55fd8bc924125/libusb_package-1.0.26.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f6d012df4942c91e6833dd251bd90c1242496a30c81020e43b98df85c66fa30", size = 71037, upload-time = "2025-04-01T14:53:06.642Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/f2/99091fdc38e916cc9e254912d1131496ac56ab82506548f6e1fc2eea8429/libusb_package-1.0.26.3-cp310-cp310-win32.whl", hash = "sha256:55c3988f622745a4874ac4face117da19969b82d51250e5334cd176f516bcb57", size = 77642, upload-time = "2025-04-01T12:58:00.114Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/9a/7bc9f60e563e535bf80b125d0d7541ad07ecb0160965d48cb8b6dccc2cf6/libusb_package-1.0.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:ba5e87e70833e5fff977d7bf12b7107df427ee21a8021d59520e1fdf14a32368", size = 90594, upload-time = "2025-04-01T12:58:01.799Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/bf/3fe9d322e2dcd0437ae2bd6a039117965702ed473ca59d2d6a1c39838009/libusb_package-1.0.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:60e15d7d3e4aab31794da95641bc28c4ffec9e24f50891ce33f75794b8f531f3", size = 63864, upload-time = "2025-04-01T12:59:14.567Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/70/df0348c11e6aaead4a66cc59840e102ddf64baf8e4b2c1ad5cff1ca83554/libusb_package-1.0.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d93a6137609cf72dc5db69bc337ddf96520231e395beeff69fa77a923090003", size = 59502, upload-time = "2025-04-01T12:59:15.863Z" },
+ { url = "https://files.pythonhosted.org/packages/27/67/6c84eebc9fcdf7f26704b5d32b51b3ee5bf4e9090d61286941257bdc8702/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fafb69c5fd42b241fbd20493d014328c507d34e1b7ceb883a20ef14565b26898", size = 70247, upload-time = "2025-04-01T14:53:07.606Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/b4/cbcc42ca4b3d8778bf081b96e6e6288a437d82a4cc4e9b982bef40a88856/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c206cd8a30565a0cede3ba426929e70a37e7b769e41a5ac7f00ca6737dc5d", size = 74537, upload-time = "2025-04-01T14:53:08.61Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/0f/fd203fb1fa5eda1d446f345d84205f23533767e6ef837a7c77a2599d5783/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a2041331c087d5887969405837f86c8422120fe9ba3e6faa44bf4810f07b71", size = 70653, upload-time = "2025-04-01T14:53:09.576Z" },
+ { url = "https://files.pythonhosted.org/packages/79/ef/dcc682cb4b29c4d4cdb23df65825c6276753184f6a7b4338c54a59a54c20/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48b536a1279ee0dbf70b898cffd16cd661774d2c8bbec8ff7178a5bc20196af3", size = 71859, upload-time = "2025-04-01T14:53:10.987Z" },
+ { url = "https://files.pythonhosted.org/packages/62/4d/323d5ac4ba339362e4b148c291fbc6e7ee04c6395d5fec967b32432db5c5/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f273e33ff1810242f81ea3a0286e25887d99d99019ba83e08be0d1ca456cc05", size = 76476, upload-time = "2025-04-01T14:53:12.019Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3b/506db7f6cbe5dc2f38c14b272b8faf4d43e5559ac99d4dce1a41026ec925/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67476093601e1ea58a6130426795b906acd8d18d51c84e29a3a69548a5dfcf5d", size = 71037, upload-time = "2025-04-01T14:53:13.42Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/40/2538763c06e07bbbe0a5c8830779ef1ed1cea845264a91973bf31b9ecce5/libusb_package-1.0.26.3-cp311-cp311-win32.whl", hash = "sha256:8f3eed2852ee4f08847a221749a98d0f4f3962f8bed967e2253327db1171ba60", size = 77642, upload-time = "2025-04-01T12:58:03.106Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/46/0cd5ea91c5bbe6293c0936c96915051e31750f72e9556718af666af3fe45/libusb_package-1.0.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:b48b5f5b17c7ac5e315e233f9ee801f730aac6183eb53a3226b01245d7bcfe00", size = 90592, upload-time = "2025-04-01T12:58:04.103Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/f6/83e13936b5799360eae8f0e31b5b298dd092451b91136d7cd13852777954/libusb_package-1.0.26.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c9404298485762a4e73b416e8a3208d33aa3274fb9b870c2a1cacba7e2918f19", size = 62045, upload-time = "2025-04-01T12:59:16.817Z" },
+ { url = "https://files.pythonhosted.org/packages/33/97/86ed73880b6734c9383be5f34061b541e8fe5bd0303580b1f5abe2962d58/libusb_package-1.0.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8126f6711318dad4cb2805ea20cd47b895a847207087d8fdb032e082dd7a2e24", size = 59502, upload-time = "2025-04-01T12:59:17.72Z" },
+ { url = "https://files.pythonhosted.org/packages/95/f7/27b67b8fe63450abf0b0b66aacf75d5d64cdf30317e214409ceb534f34b4/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11c219366e4a2368117b9a9807261f3506b5623531f8b8ce41af5bbaec8156a0", size = 70247, upload-time = "2025-04-01T14:53:14.387Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/11/613543f9c6dab5a82eefd0c78d52d08b5d9eb93a0362151fbedf74b32541/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8809a50d8ab84297344c54e862027090c0d73b14abef843a8b5f783313f49457", size = 74537, upload-time = "2025-04-01T14:53:15.345Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/43/5a2331615693b56221a902869fb2094d9a0b9a764a8706c8ba16e915f77c/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a83067c3dfdbb3856badb4532eaea22e8502b52ce4245f5ab46acf93d7fbd471", size = 70652, upload-time = "2025-04-01T14:53:16.319Z" },
+ { url = "https://files.pythonhosted.org/packages/44/1a/186d4ec86421b69feb45e214edb5301fbcb9e8dc9df963678aeff1a447d5/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b56be087ea9cde8e50fb02740a4f0cefb6f63c61ac2e7812a9244487614a3973", size = 71860, upload-time = "2025-04-01T14:53:17.87Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/3c/8cebdad822d7bfcb683a77d5fd113fbc6f72516cfb7c1c3a274fefafa8e9/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea0f6bf40e54b1671e763e40c9dbed46bf7f596a4cd98b7c827e147f176d8c97", size = 76476, upload-time = "2025-04-01T14:53:19.202Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5f/30c625b6c4ecd14871644c1d16e97d7c971f82a0f87a9cfa81022f85bcfc/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b40f77df991c6db8621de9575504886eca03a00277e521a4d64b66cbef8f6997", size = 71037, upload-time = "2025-04-01T14:53:21.359Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/e9/3aa3ff3242867b7f22ee3ce28d0e93ff88547f170ca1b8a6edc59660d974/libusb_package-1.0.26.3-cp312-cp312-win32.whl", hash = "sha256:6eee99c9fde137443869c8604d0c01b2127a9545ebc59d06a3376cf1d891e786", size = 77642, upload-time = "2025-04-01T12:58:05.471Z" },
+ { url = "https://files.pythonhosted.org/packages/15/0e/913ddb1849f828fc385438874c34541939d9b06c0e5616f48f24cddd24de/libusb_package-1.0.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:5e09c0b6b3cd475841cffe78e46e91df58f0c6c02ea105ea1a4d0755a07c8006", size = 90593, upload-time = "2025-04-01T12:58:06.798Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/b8/23bc7f3f53b4a5b1027c721ec3eb42324ca1ec56355f0d0851307adc7c6c/libusb_package-1.0.26.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:04c4505e2ca68d3dc6938f116ff9bf82daffb06c1a97aba08293a84715a998da", size = 62045, upload-time = "2025-04-01T12:59:18.698Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/f8/e3be96d0604070488ddc5ce5af1976992e1f4a00e6441c94edf807f274d5/libusb_package-1.0.26.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4961cdb3c622aa9f858d3e4f99a58ce5e822a97c22abc77040fd806cb5fa4c66", size = 59502, upload-time = "2025-04-01T12:59:19.632Z" },
+ { url = "https://files.pythonhosted.org/packages/24/d5/df1508df5e6776ac8a09a2858991df29bc96ea6a0d1f90240b1c4d59b45d/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16182670e0c23235521b447054c5a01600bd8f1eed3bb08eedbb0d9f8a43249f", size = 70247, upload-time = "2025-04-01T14:53:22.328Z" },
+ { url = "https://files.pythonhosted.org/packages/65/01/4cc9eed12b9214c088cfa8055ece3b1db970404400be9d7e3dda68d198f2/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75ea57b2cc903d28ec1d4b909902df442cbf21949d80d5b3d8b9dac36ac45d1a", size = 74537, upload-time = "2025-04-01T14:53:23.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/83/9eb317f706f588f4b6679bddb8abee3b115ce53dc3fa560cca59910f8807/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d30b51b128ef5112fff73268b4696fea00b5676b3f39a5ee859bd76cb3ace5", size = 70651, upload-time = "2025-04-01T14:53:24.33Z" },
+ { url = "https://files.pythonhosted.org/packages/22/49/85d3b307b4a20cf0150ab381e6e0385e5b78cb5dede8bade0a2d655d3fd3/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c098dcfcfa8000cab42f33e19628c8fdb16111670db381048b2993651f2413b", size = 71860, upload-time = "2025-04-01T14:53:25.752Z" },
+ { url = "https://files.pythonhosted.org/packages/da/7a/2271a5ae542d9036d9254415ae745d5c5d01a08d56d13054b2439bf9d392/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:93169aeab0657255fe6c9f757cf408f559db13827a1d122fc89239994d7d51f1", size = 76477, upload-time = "2025-04-01T14:53:27.564Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9d/d06d53994bb164564ec142ef631a4afa31e324994cf223f169ecca127f3a/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:63257653ee1ee06aa836e942f4bb89a1d7a0c6ae3d6183647a9011e585ffa1e3", size = 71036, upload-time = "2025-04-01T14:53:29.011Z" },
+ { url = "https://files.pythonhosted.org/packages/32/3d/97f775a1d582548b1eb2a42444c58813e5fd93d568fc3b9ace59f64df527/libusb_package-1.0.26.3-cp313-cp313-win32.whl", hash = "sha256:05db4cc801db2e6373a808725748a701509f9450fecf393fbebab61c45d50b50", size = 77642, upload-time = "2025-04-01T12:58:07.774Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c4/d5234607697ca60593fbef88428a154317ac31f5c58ee23337b8a9360e91/libusb_package-1.0.26.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cd4aec825dac2b4fa5d23b37f6d72e63a1127987e5a073dabeb7b73528623a3", size = 90593, upload-time = "2025-04-01T12:58:08.676Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/20/f5293a167b4e910badc64272131a8bb8dbd80f10dfd843eb07846aafaef2/libusb_package-1.0.26.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6a62bf7fa20fe704ed0413e74d620b37bdfe6b084478d23cc85b1f10708f2596", size = 62194, upload-time = "2025-04-01T12:59:26.137Z" },
+ { url = "https://files.pythonhosted.org/packages/28/e5/1ceae06e6c965847d89be36de58908354c35faf641cd4c6071c9f06a7e9b/libusb_package-1.0.26.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab40d6b295bcfbe37f280268ea0d0a1ef4b1d795025fe41b3dda48e07eb0fc8e", size = 59506, upload-time = "2025-04-01T12:59:27.081Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/74/8afb1a05fda665abebac3bb44a7738f23437cac11081e44a929b51afee6a/libusb_package-1.0.26.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a125d72cca545950ae357aa6d7f0f33dfb39f16b895691cf3f8c9b772bc7e31", size = 70255, upload-time = "2025-04-01T14:53:48.956Z" },
+ { url = "https://files.pythonhosted.org/packages/35/46/5e6be05f302e887055a277bbb5cc1db6be9af01319b35f1a9663211b075c/libusb_package-1.0.26.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:229d9f26af5828512828154d6bae4214ef5016a9401dd022477e06d0df5153e7", size = 74543, upload-time = "2025-04-01T14:53:49.979Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e4/51a81cc69ba4eefdd9a291cc5e6596a8f7d8c7f2378273917bf64465412d/libusb_package-1.0.26.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b8b862f190c244f29699d783f044e3d816fed84e39bca9a3c3731140f0b1b39", size = 70658, upload-time = "2025-04-01T14:53:51.132Z" },
+ { url = "https://files.pythonhosted.org/packages/15/14/2c85379880d475f12ee74a27b02a2ffe435d863f8045fe80e5c246c30f23/libusb_package-1.0.26.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fadaad1181784713948f9cbb7ad1cab8f2b307e784e2e162ed80ba5d2f745901", size = 90602, upload-time = "2025-04-01T12:58:16.828Z" },
+]
+
+[[package]]
+name = "log-throttling"
+version = "0.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/be/09fda383d82a7b91f12ff5002bc909e101769c802ff3b1d2cb9f533d1a2f/log-throttling-0.0.3.tar.gz", hash = "sha256:df714e1448cd9cb4c916cf668ab88323032e61ece733548708d996ecf63190b9", size = 3533, upload-time = "2022-03-08T16:29:23.674Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/6e/995ebad4ab08250b4afe1d91fe3086b863ed9bb74539d90485250a50f08a/log_throttling-0.0.3-py3-none-any.whl", hash = "sha256:a82508f489dd44a60b737a0339c784dd8ee2b9db16bdcf587a8ae1012a7f6ffc", size = 4081, upload-time = "2022-03-08T16:29:21.443Z" },
+]
+
+[[package]]
+name = "lxml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/8a/f8192a08237ef2fb1b19733f709db88a4c43bc8ab8357f01cb41a27e7f6a/lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388", size = 8590589, upload-time = "2025-09-22T04:00:10.51Z" },
+ { url = "https://files.pythonhosted.org/packages/12/64/27bcd07ae17ff5e5536e8d88f4c7d581b48963817a13de11f3ac3329bfa2/lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153", size = 4629671, upload-time = "2025-09-22T04:00:15.411Z" },
+ { url = "https://files.pythonhosted.org/packages/02/5a/a7d53b3291c324e0b6e48f3c797be63836cc52156ddf8f33cd72aac78866/lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31", size = 4999961, upload-time = "2025-09-22T04:00:17.619Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/55/d465e9b89df1761674d8672bb3e4ae2c47033b01ec243964b6e334c6743f/lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9", size = 5157087, upload-time = "2025-09-22T04:00:19.868Z" },
+ { url = "https://files.pythonhosted.org/packages/62/38/3073cd7e3e8dfc3ba3c3a139e33bee3a82de2bfb0925714351ad3d255c13/lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8", size = 5067620, upload-time = "2025-09-22T04:00:21.877Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d3/1e001588c5e2205637b08985597827d3827dbaaece16348c8822bfe61c29/lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba", size = 5406664, upload-time = "2025-09-22T04:00:23.714Z" },
+ { url = "https://files.pythonhosted.org/packages/20/cf/cab09478699b003857ed6ebfe95e9fb9fa3d3c25f1353b905c9b73cfb624/lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c", size = 5289397, upload-time = "2025-09-22T04:00:25.544Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/84/02a2d0c38ac9a8b9f9e5e1bbd3f24b3f426044ad618b552e9549ee91bd63/lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c", size = 4772178, upload-time = "2025-09-22T04:00:27.602Z" },
+ { url = "https://files.pythonhosted.org/packages/56/87/e1ceadcc031ec4aa605fe95476892d0b0ba3b7f8c7dcdf88fdeff59a9c86/lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321", size = 5358148, upload-time = "2025-09-22T04:00:29.323Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/13/5bb6cf42bb228353fd4ac5f162c6a84fd68a4d6f67c1031c8cf97e131fc6/lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1", size = 5112035, upload-time = "2025-09-22T04:00:31.061Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/e2/ea0498552102e59834e297c5c6dff8d8ded3db72ed5e8aad77871476f073/lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34", size = 4799111, upload-time = "2025-09-22T04:00:33.11Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/9e/8de42b52a73abb8af86c66c969b3b4c2a96567b6ac74637c037d2e3baa60/lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a", size = 5351662, upload-time = "2025-09-22T04:00:35.237Z" },
+ { url = "https://files.pythonhosted.org/packages/28/a2/de776a573dfb15114509a37351937c367530865edb10a90189d0b4b9b70a/lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c", size = 5314973, upload-time = "2025-09-22T04:00:37.086Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a0/3ae1b1f8964c271b5eec91db2043cf8c6c0bce101ebb2a633b51b044db6c/lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b", size = 3611953, upload-time = "2025-09-22T04:00:39.224Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/70/bd42491f0634aad41bdfc1e46f5cff98825fb6185688dc82baa35d509f1a/lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0", size = 4032695, upload-time = "2025-09-22T04:00:41.402Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d0/05c6a72299f54c2c561a6c6cbb2f512e047fca20ea97a05e57931f194ac4/lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5", size = 3680051, upload-time = "2025-09-22T04:00:43.525Z" },
+ { url = "https://files.pythonhosted.org/packages/77/d5/becbe1e2569b474a23f0c672ead8a29ac50b2dc1d5b9de184831bda8d14c/lxml-6.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:13e35cbc684aadf05d8711a5d1b5857c92e5e580efa9a0d2be197199c8def607", size = 8634365, upload-time = "2025-09-22T04:00:45.672Z" },
+ { url = "https://files.pythonhosted.org/packages/28/66/1ced58f12e804644426b85d0bb8a4478ca77bc1761455da310505f1a3526/lxml-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1675e096e17c6fe9c0e8c81434f5736c0739ff9ac6123c87c2d452f48fc938", size = 4650793, upload-time = "2025-09-22T04:00:47.783Z" },
+ { url = "https://files.pythonhosted.org/packages/11/84/549098ffea39dfd167e3f174b4ce983d0eed61f9d8d25b7bf2a57c3247fc/lxml-6.0.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac6e5811ae2870953390452e3476694196f98d447573234592d30488147404d", size = 4944362, upload-time = "2025-09-22T04:00:49.845Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/bd/f207f16abf9749d2037453d56b643a7471d8fde855a231a12d1e095c4f01/lxml-6.0.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5aa0fc67ae19d7a64c3fe725dc9a1bb11f80e01f78289d05c6f62545affec438", size = 5083152, upload-time = "2025-09-22T04:00:51.709Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ae/bd813e87d8941d52ad5b65071b1affb48da01c4ed3c9c99e40abb266fbff/lxml-6.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de496365750cc472b4e7902a485d3f152ecf57bd3ba03ddd5578ed8ceb4c5964", size = 5023539, upload-time = "2025-09-22T04:00:53.593Z" },
+ { url = "https://files.pythonhosted.org/packages/02/cd/9bfef16bd1d874fbe0cb51afb00329540f30a3283beb9f0780adbb7eec03/lxml-6.0.2-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:200069a593c5e40b8f6fc0d84d86d970ba43138c3e68619ffa234bc9bb806a4d", size = 5344853, upload-time = "2025-09-22T04:00:55.524Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/89/ea8f91594bc5dbb879734d35a6f2b0ad50605d7fb419de2b63d4211765cc/lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d2de809c2ee3b888b59f995625385f74629707c9355e0ff856445cdcae682b7", size = 5225133, upload-time = "2025-09-22T04:00:57.269Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/37/9c735274f5dbec726b2db99b98a43950395ba3d4a1043083dba2ad814170/lxml-6.0.2-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:b2c3da8d93cf5db60e8858c17684c47d01fee6405e554fb55018dd85fc23b178", size = 4677944, upload-time = "2025-09-22T04:00:59.052Z" },
+ { url = "https://files.pythonhosted.org/packages/20/28/7dfe1ba3475d8bfca3878365075abe002e05d40dfaaeb7ec01b4c587d533/lxml-6.0.2-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:442de7530296ef5e188373a1ea5789a46ce90c4847e597856570439621d9c553", size = 5284535, upload-time = "2025-09-22T04:01:01.335Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/cf/5f14bc0de763498fc29510e3532bf2b4b3a1c1d5d0dff2e900c16ba021ef/lxml-6.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2593c77efde7bfea7f6389f1ab249b15ed4aa5bc5cb5131faa3b843c429fbedb", size = 5067343, upload-time = "2025-09-22T04:01:03.13Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/b0/bb8275ab5472f32b28cfbbcc6db7c9d092482d3439ca279d8d6fa02f7025/lxml-6.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3e3cb08855967a20f553ff32d147e14329b3ae70ced6edc2f282b94afbc74b2a", size = 4725419, upload-time = "2025-09-22T04:01:05.013Z" },
+ { url = "https://files.pythonhosted.org/packages/25/4c/7c222753bc72edca3b99dbadba1b064209bc8ed4ad448af990e60dcce462/lxml-6.0.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ed6c667fcbb8c19c6791bbf40b7268ef8ddf5a96940ba9404b9f9a304832f6c", size = 5275008, upload-time = "2025-09-22T04:01:07.327Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/8c/478a0dc6b6ed661451379447cdbec77c05741a75736d97e5b2b729687828/lxml-6.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b8f18914faec94132e5b91e69d76a5c1d7b0c73e2489ea8929c4aaa10b76bbf7", size = 5248906, upload-time = "2025-09-22T04:01:09.452Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/d9/5be3a6ab2784cdf9accb0703b65e1b64fcdd9311c9f007630c7db0cfcce1/lxml-6.0.2-cp311-cp311-win32.whl", hash = "sha256:6605c604e6daa9e0d7f0a2137bdc47a2e93b59c60a65466353e37f8272f47c46", size = 3610357, upload-time = "2025-09-22T04:01:11.102Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/7d/ca6fb13349b473d5732fb0ee3eec8f6c80fc0688e76b7d79c1008481bf1f/lxml-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e5867f2651016a3afd8dd2c8238baa66f1e2802f44bc17e236f547ace6647078", size = 4036583, upload-time = "2025-09-22T04:01:12.766Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a2/51363b5ecd3eab46563645f3a2c3836a2fc67d01a1b87c5017040f39f567/lxml-6.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:4197fb2534ee05fd3e7afaab5d8bfd6c2e186f65ea7f9cd6a82809c887bd1285", size = 3680591, upload-time = "2025-09-22T04:01:14.874Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/c8/8ff2bc6b920c84355146cd1ab7d181bc543b89241cfb1ebee824a7c81457/lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456", size = 8661887, upload-time = "2025-09-22T04:01:17.265Z" },
+ { url = "https://files.pythonhosted.org/packages/37/6f/9aae1008083bb501ef63284220ce81638332f9ccbfa53765b2b7502203cf/lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924", size = 4667818, upload-time = "2025-09-22T04:01:19.688Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/ca/31fb37f99f37f1536c133476674c10b577e409c0a624384147653e38baf2/lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f", size = 4950807, upload-time = "2025-09-22T04:01:21.487Z" },
+ { url = "https://files.pythonhosted.org/packages/da/87/f6cb9442e4bada8aab5ae7e1046264f62fdbeaa6e3f6211b93f4c0dd97f1/lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534", size = 5109179, upload-time = "2025-09-22T04:01:23.32Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/20/a7760713e65888db79bbae4f6146a6ae5c04e4a204a3c48896c408cd6ed2/lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564", size = 5023044, upload-time = "2025-09-22T04:01:25.118Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/b0/7e64e0460fcb36471899f75831509098f3fd7cd02a3833ac517433cb4f8f/lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f", size = 5359685, upload-time = "2025-09-22T04:01:27.398Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/e1/e5df362e9ca4e2f48ed6411bd4b3a0ae737cc842e96877f5bf9428055ab4/lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0", size = 5654127, upload-time = "2025-09-22T04:01:29.629Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/d1/232b3309a02d60f11e71857778bfcd4acbdb86c07db8260caf7d008b08f8/lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192", size = 5253958, upload-time = "2025-09-22T04:01:31.535Z" },
+ { url = "https://files.pythonhosted.org/packages/35/35/d955a070994725c4f7d80583a96cab9c107c57a125b20bb5f708fe941011/lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0", size = 4711541, upload-time = "2025-09-22T04:01:33.801Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/be/667d17363b38a78c4bd63cfd4b4632029fd68d2c2dc81f25ce9eb5224dd5/lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092", size = 5267426, upload-time = "2025-09-22T04:01:35.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/62c70aa4a1c26569bc958c9ca86af2bb4e1f614e8c04fb2989833874f7ae/lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f", size = 5064917, upload-time = "2025-09-22T04:01:37.448Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/55/6ceddaca353ebd0f1908ef712c597f8570cc9c58130dbb89903198e441fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8", size = 4788795, upload-time = "2025-09-22T04:01:39.165Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e8/fd63e15da5e3fd4c2146f8bbb3c14e94ab850589beab88e547b2dbce22e1/lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f", size = 5676759, upload-time = "2025-09-22T04:01:41.506Z" },
+ { url = "https://files.pythonhosted.org/packages/76/47/b3ec58dc5c374697f5ba37412cd2728f427d056315d124dd4b61da381877/lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6", size = 5255666, upload-time = "2025-09-22T04:01:43.363Z" },
+ { url = "https://files.pythonhosted.org/packages/19/93/03ba725df4c3d72afd9596eef4a37a837ce8e4806010569bedfcd2cb68fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322", size = 5277989, upload-time = "2025-09-22T04:01:45.215Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/80/c06de80bfce881d0ad738576f243911fccf992687ae09fd80b734712b39c/lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849", size = 3611456, upload-time = "2025-09-22T04:01:48.243Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/d7/0cdfb6c3e30893463fb3d1e52bc5f5f99684a03c29a0b6b605cfae879cd5/lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f", size = 4011793, upload-time = "2025-09-22T04:01:50.042Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/7b/93c73c67db235931527301ed3785f849c78991e2e34f3fd9a6663ffda4c5/lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6", size = 3672836, upload-time = "2025-09-22T04:01:52.145Z" },
+ { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" },
+ { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" },
+ { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" },
+ { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" },
+ { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" },
+ { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" },
+ { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" },
+ { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" },
+ { url = "https://files.pythonhosted.org/packages/03/15/d4a377b385ab693ce97b472fe0c77c2b16ec79590e688b3ccc71fba19884/lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe", size = 8659801, upload-time = "2025-09-22T04:02:30.113Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/e8/c128e37589463668794d503afaeb003987373c5f94d667124ffd8078bbd9/lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d", size = 4659403, upload-time = "2025-09-22T04:02:32.119Z" },
+ { url = "https://files.pythonhosted.org/packages/00/ce/74903904339decdf7da7847bb5741fc98a5451b42fc419a86c0c13d26fe2/lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d", size = 4966974, upload-time = "2025-09-22T04:02:34.155Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/d3/131dec79ce61c5567fecf82515bd9bc36395df42501b50f7f7f3bd065df0/lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5", size = 5102953, upload-time = "2025-09-22T04:02:36.054Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/ea/a43ba9bb750d4ffdd885f2cd333572f5bb900cd2408b67fdda07e85978a0/lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0", size = 5055054, upload-time = "2025-09-22T04:02:38.154Z" },
+ { url = "https://files.pythonhosted.org/packages/60/23/6885b451636ae286c34628f70a7ed1fcc759f8d9ad382d132e1c8d3d9bfd/lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba", size = 5352421, upload-time = "2025-09-22T04:02:40.413Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5b/fc2ddfc94ddbe3eebb8e9af6e3fd65e2feba4967f6a4e9683875c394c2d8/lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0", size = 5673684, upload-time = "2025-09-22T04:02:42.288Z" },
+ { url = "https://files.pythonhosted.org/packages/29/9c/47293c58cc91769130fbf85531280e8cc7868f7fbb6d92f4670071b9cb3e/lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d", size = 5252463, upload-time = "2025-09-22T04:02:44.165Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/da/ba6eceb830c762b48e711ded880d7e3e89fc6c7323e587c36540b6b23c6b/lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37", size = 4698437, upload-time = "2025-09-22T04:02:46.524Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/24/7be3f82cb7990b89118d944b619e53c656c97dc89c28cfb143fdb7cd6f4d/lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9", size = 5269890, upload-time = "2025-09-22T04:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/bd/dcfb9ea1e16c665efd7538fc5d5c34071276ce9220e234217682e7d2c4a5/lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917", size = 5097185, upload-time = "2025-09-22T04:02:50.746Z" },
+ { url = "https://files.pythonhosted.org/packages/21/04/a60b0ff9314736316f28316b694bccbbabe100f8483ad83852d77fc7468e/lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f", size = 4745895, upload-time = "2025-09-22T04:02:52.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/bd/7d54bd1846e5a310d9c715921c5faa71cf5c0853372adf78aee70c8d7aa2/lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8", size = 5695246, upload-time = "2025-09-22T04:02:54.798Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/32/5643d6ab947bc371da21323acb2a6e603cedbe71cb4c99c8254289ab6f4e/lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a", size = 5260797, upload-time = "2025-09-22T04:02:57.058Z" },
+ { url = "https://files.pythonhosted.org/packages/33/da/34c1ec4cff1eea7d0b4cd44af8411806ed943141804ac9c5d565302afb78/lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c", size = 5277404, upload-time = "2025-09-22T04:02:58.966Z" },
+ { url = "https://files.pythonhosted.org/packages/82/57/4eca3e31e54dc89e2c3507e1cd411074a17565fa5ffc437c4ae0a00d439e/lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b", size = 3670072, upload-time = "2025-09-22T04:03:38.05Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/e0/c96cf13eccd20c9421ba910304dae0f619724dcf1702864fd59dd386404d/lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed", size = 4080617, upload-time = "2025-09-22T04:03:39.835Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/5d/b3f03e22b3d38d6f188ef044900a9b29b2fe0aebb94625ce9fe244011d34/lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8", size = 3754930, upload-time = "2025-09-22T04:03:41.565Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/5c/42c2c4c03554580708fc738d13414801f340c04c3eff90d8d2d227145275/lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d", size = 8910380, upload-time = "2025-09-22T04:03:01.645Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/4f/12df843e3e10d18d468a7557058f8d3733e8b6e12401f30b1ef29360740f/lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba", size = 4775632, upload-time = "2025-09-22T04:03:03.814Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/0c/9dc31e6c2d0d418483cbcb469d1f5a582a1cd00a1f4081953d44051f3c50/lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601", size = 4975171, upload-time = "2025-09-22T04:03:05.651Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/2b/9b870c6ca24c841bdd887504808f0417aa9d8d564114689266f19ddf29c8/lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed", size = 5110109, upload-time = "2025-09-22T04:03:07.452Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/0c/4f5f2a4dd319a178912751564471355d9019e220c20d7db3fb8307ed8582/lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37", size = 5041061, upload-time = "2025-09-22T04:03:09.297Z" },
+ { url = "https://files.pythonhosted.org/packages/12/64/554eed290365267671fe001a20d72d14f468ae4e6acef1e179b039436967/lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338", size = 5306233, upload-time = "2025-09-22T04:03:11.651Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/31/1d748aa275e71802ad9722df32a7a35034246b42c0ecdd8235412c3396ef/lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9", size = 5604739, upload-time = "2025-09-22T04:03:13.592Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/41/2c11916bcac09ed561adccacceaedd2bf0e0b25b297ea92aab99fd03d0fa/lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd", size = 5225119, upload-time = "2025-09-22T04:03:15.408Z" },
+ { url = "https://files.pythonhosted.org/packages/99/05/4e5c2873d8f17aa018e6afde417c80cc5d0c33be4854cce3ef5670c49367/lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d", size = 4633665, upload-time = "2025-09-22T04:03:17.262Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/c9/dcc2da1bebd6275cdc723b515f93edf548b82f36a5458cca3578bc899332/lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9", size = 5234997, upload-time = "2025-09-22T04:03:19.14Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e2/5172e4e7468afca64a37b81dba152fc5d90e30f9c83c7c3213d6a02a5ce4/lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e", size = 5090957, upload-time = "2025-09-22T04:03:21.436Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/b3/15461fd3e5cd4ddcb7938b87fc20b14ab113b92312fc97afe65cd7c85de1/lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d", size = 4764372, upload-time = "2025-09-22T04:03:23.27Z" },
+ { url = "https://files.pythonhosted.org/packages/05/33/f310b987c8bf9e61c4dd8e8035c416bd3230098f5e3cfa69fc4232de7059/lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec", size = 5634653, upload-time = "2025-09-22T04:03:25.767Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ff/51c80e75e0bc9382158133bdcf4e339b5886c6ee2418b5199b3f1a61ed6d/lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272", size = 5233795, upload-time = "2025-09-22T04:03:27.62Z" },
+ { url = "https://files.pythonhosted.org/packages/56/4d/4856e897df0d588789dd844dbed9d91782c4ef0b327f96ce53c807e13128/lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f", size = 5257023, upload-time = "2025-09-22T04:03:30.056Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/85/86766dfebfa87bea0ab78e9ff7a4b4b45225df4b4d3b8cc3c03c5cd68464/lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312", size = 3911420, upload-time = "2025-09-22T04:03:32.198Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/1a/b248b355834c8e32614650b8008c69ffeb0ceb149c793961dd8c0b991bb3/lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca", size = 4406837, upload-time = "2025-09-22T04:03:34.027Z" },
+ { url = "https://files.pythonhosted.org/packages/92/aa/df863bcc39c5e0946263454aba394de8a9084dbaff8ad143846b0d844739/lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c", size = 3822205, upload-time = "2025-09-22T04:03:36.249Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/9c/780c9a8fce3f04690b374f72f41306866b0400b9d0fdf3e17aaa37887eed/lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6", size = 3939264, upload-time = "2025-09-22T04:04:32.892Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/5a/1ab260c00adf645d8bf7dec7f920f744b032f69130c681302821d5debea6/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba", size = 4216435, upload-time = "2025-09-22T04:04:34.907Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/37/565f3b3d7ffede22874b6d86be1a1763d00f4ea9fc5b9b6ccb11e4ec8612/lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5", size = 4325913, upload-time = "2025-09-22T04:04:37.205Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ec/f3a1b169b2fb9d03467e2e3c0c752ea30e993be440a068b125fc7dd248b0/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4", size = 4269357, upload-time = "2025-09-22T04:04:39.322Z" },
+ { url = "https://files.pythonhosted.org/packages/77/a2/585a28fe3e67daa1cf2f06f34490d556d121c25d500b10082a7db96e3bcd/lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d", size = 4412295, upload-time = "2025-09-22T04:04:41.647Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/d9/a57dd8bcebd7c69386c20263830d4fa72d27e6b72a229ef7a48e88952d9a/lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d", size = 3516913, upload-time = "2025-09-22T04:04:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/11/29d08bc103a62c0eba8016e7ed5aeebbf1e4312e83b0b1648dd203b0e87d/lxml-6.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c06035eafa8404b5cf475bb37a9f6088b0aca288d4ccc9d69389750d5543700", size = 3949829, upload-time = "2025-09-22T04:04:45.608Z" },
+ { url = "https://files.pythonhosted.org/packages/12/b3/52ab9a3b31e5ab8238da241baa19eec44d2ab426532441ee607165aebb52/lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c7d13103045de1bdd6fe5d61802565f1a3537d70cd3abf596aa0af62761921ee", size = 4226277, upload-time = "2025-09-22T04:04:47.754Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/33/1eaf780c1baad88224611df13b1c2a9dfa460b526cacfe769103ff50d845/lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a3c150a95fbe5ac91de323aa756219ef9cf7fde5a3f00e2281e30f33fa5fa4f", size = 4330433, upload-time = "2025-09-22T04:04:49.907Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/c1/27428a2ff348e994ab4f8777d3a0ad510b6b92d37718e5887d2da99952a2/lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60fa43be34f78bebb27812ed90f1925ec99560b0fa1decdb7d12b84d857d31e9", size = 4272119, upload-time = "2025-09-22T04:04:51.801Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d0/3020fa12bcec4ab62f97aab026d57c2f0cfd480a558758d9ca233bb6a79d/lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21c73b476d3cfe836be731225ec3421fa2f048d84f6df6a8e70433dff1376d5a", size = 4417314, upload-time = "2025-09-22T04:04:55.024Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/77/d7f491cbc05303ac6801651aabeb262d43f319288c1ea96c66b1d2692ff3/lxml-6.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:27220da5be049e936c3aca06f174e8827ca6445a4353a1995584311487fc4e3e", size = 3518768, upload-time = "2025-09-22T04:04:57.097Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" },
+ { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" },
+ { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" },
+ { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" },
+ { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" },
+ { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" },
+ { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" },
+ { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" },
+ { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" },
+ { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" },
+ { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
+ { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
+ { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
+ { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
+ { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
+ { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
+ { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
+ { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
+ { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
+]
+
+[[package]]
+name = "matplotlib-inline"
+version = "0.1.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" },
+]
+
+[[package]]
+name = "meshcat"
+version = "0.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "ipython", version = "9.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "numpy" },
+ { name = "pillow" },
+ { name = "pyngrok" },
+ { name = "pyzmq" },
+ { name = "tornado" },
+ { name = "u-msgpack-python" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/c2/fae203e8e2119d4c793c9d7d2b8d1fea10752bb39b7b330beb8e67b6117c/meshcat-0.3.2.tar.gz", hash = "sha256:2cfe17cde4fe85d072a3de018a9dabf5626f80c255fff2ce63b26a48d2484ad9", size = 611361, upload-time = "2021-11-07T20:31:55.305Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a8/c3/5f2b53fd7d43abb8c67015c737569303f39f43aa2b5a4c0add860be5d7e8/meshcat-0.3.2-py3-none-any.whl", hash = "sha256:cba0465d0e29658c48bb65d27f5c8e04ff0a1ac8ce916d65621146c06258a1c7", size = 2640524, upload-time = "2021-11-07T20:31:53.033Z" },
+]
+
+[[package]]
+name = "mpmath"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
+]
+
+[[package]]
+name = "mujoco"
+version = "3.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "absl-py" },
+ { name = "etils", extra = ["epath"] },
+ { name = "glfw" },
+ { name = "numpy" },
+ { name = "pyopengl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8a/c6/76716fd205ccd0afbaef8b355555ace70347bd02c921d14e05e68eab3dbb/mujoco-3.3.0.tar.gz", hash = "sha256:0fb12ab715bc2b6d2394a6330b8c3a1dd95b0f3ba812bfe4f6c91e0a1ca3ad0f", size = 801447, upload-time = "2025-02-27T19:16:18.748Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/f4/21ff1dc849ff6ab8f2fc29d6d6286ab5e03c25ac7cced3bcbbe052c257be/mujoco-3.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:676285e5a752e5b937fc79ff56cad6d62021992e6800d8085ab316c67d7b0ee4", size = 6484740, upload-time = "2025-02-27T19:14:36.642Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/3b/22acd7de2cf66839a3e49b57cc428ab6816764f128209a80578403eac83d/mujoco-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6991fe6b255eabf5c62c8a3dffa2e04a51bd74d04de42869c322da1c4279562", size = 6512230, upload-time = "2025-02-27T19:14:40.481Z" },
+ { url = "https://files.pythonhosted.org/packages/38/5b/b257c5a6435258a4ce908befa7131b94991ecf95639a58e14daf616d5edd/mujoco-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dd0db1edaabf4a1beccdacfe9c2c313eb47b25bc4016d9a2f97771d0e9dfa89", size = 6261748, upload-time = "2025-02-27T19:14:45.42Z" },
+ { url = "https://files.pythonhosted.org/packages/71/75/f9c7fcf11f878213c2bd5af69dd6d73914efe3a60ac031c6e3e305a392db/mujoco-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:946ee6c90c4f6c4ef26502cc8a469b3dc7bd7286d8efb3dc935fa0bf8a3a11e5", size = 6549401, upload-time = "2025-02-27T19:14:49.209Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/b3/aec72a24b473394c4f6ed1cc9734f11721dd8eda354c85a768e3e9bf1019/mujoco-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:d84988f3c7e2dc2238a84c565f8c30b7e66caddd9d95b4f0d291902b6af749f3", size = 4835623, upload-time = "2025-02-27T19:14:52.732Z" },
+ { url = "https://files.pythonhosted.org/packages/11/aa/a0b20e86e9944fdbdbea004974df1426f4bc532c9711112012e89a56a389/mujoco-3.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:d61157a53356c790ef73d4ec918288c2dd7d0da26eaa1de8f56cffbdcd1ca275", size = 6498398, upload-time = "2025-02-27T19:14:59.241Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/12/5a158a125114e35409e60dedeb694bf80d7e3a02c99edc8734ed352baff2/mujoco-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:60025a554e444a85d3183714115f48ea4582c5c654f120c5f7652e22260b0069", size = 6524031, upload-time = "2025-02-27T19:15:04.301Z" },
+ { url = "https://files.pythonhosted.org/packages/81/85/f7dbcc704710e304cd1220fcdb505e5f0d142c741850efaf33950ab5bc51/mujoco-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff984aaec2006effe12fbb8bc29fbc0abb82b4332891307d99e637791067e912", size = 6276343, upload-time = "2025-02-27T19:15:08.446Z" },
+ { url = "https://files.pythonhosted.org/packages/05/9e/a0cd11d0501421d547d74ad0de092912c2e4952b9663f7807e32a9cbcf1b/mujoco-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1403630afb922ab2cb7aa7310b5cb596a5568ee4250f96e663c9f365353cab00", size = 6565173, upload-time = "2025-02-27T19:15:14.112Z" },
+ { url = "https://files.pythonhosted.org/packages/03/56/f5d8e2ac2df979044cb18c8862de7be9c2247921f8f7b06d45d3daf99c15/mujoco-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:436d863747fea327393a46f04d7aeaea6acf27b961b54dbdde32a823e90355eb", size = 4860052, upload-time = "2025-02-27T19:15:18.631Z" },
+ { url = "https://files.pythonhosted.org/packages/11/6f/218bf9c8480340d640d4f8e09c0295bfd02cc08610bd6798fad12ebc9cc0/mujoco-3.3.0-cp312-cp312-macosx_10_16_x86_64.whl", hash = "sha256:32a7671069799e69f51bf515f6f97d8b2c852a0de065129033347e616fe4e9ed", size = 6588848, upload-time = "2025-02-27T19:15:22.124Z" },
+ { url = "https://files.pythonhosted.org/packages/42/3b/f17690dee3f3f3771e9953ffe634d0a1649af44c609c8b201325b9a7fbe6/mujoco-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f435f34ea8c41711e68c635ada89c7347e9d53a330c2ff748723a96495d8710", size = 6493393, upload-time = "2025-02-27T19:15:26.398Z" },
+ { url = "https://files.pythonhosted.org/packages/db/d5/89feffffdc085a6e3a31eea46a86efdd5a4d3421dacfba742f75e3df3319/mujoco-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f526587b05879842e7380feda0a207455e78cdc05c8672c77d292383c145ab7e", size = 6283111, upload-time = "2025-02-27T19:15:30.509Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/7b/dfedcfd4a8db4d861747e34e47d96dbcbb994db47b6bd626ed3f25935b81/mujoco-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607df8af9e01831d624fc5ffcb6376899fb47c1e999a0e38b8388622da712f64", size = 6639670, upload-time = "2025-02-27T19:15:33.889Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e2/7865204cbe00a2c319bf39a918c975a2cfb931ff2715348a815bdb7b453e/mujoco-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e8811d854a5f4b3340ba11220781427659d5a87ced2d60688e476db8f3f6188", size = 4905647, upload-time = "2025-02-27T19:15:37.421Z" },
+ { url = "https://files.pythonhosted.org/packages/26/bc/0ee22fa1451b7e5b6194973c2427e58ddb347b52519ac1ecd75d7040859f/mujoco-3.3.0-cp313-cp313-macosx_10_16_x86_64.whl", hash = "sha256:f4ffdc689319a8d5e692ef19634893d29e2b88fc76b43493a958463047cd9a05", size = 6589087, upload-time = "2025-02-27T19:15:40.84Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/14/7c1bd9cfabf757bcb76dcec2b3ab556243450e587aeb639574653305a4e9/mujoco-3.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a748b12f2f39e381770bbf2674ddc4d3c63c74c3c0020efbc98afe712c8e358e", size = 6493094, upload-time = "2025-02-27T19:15:43.439Z" },
+ { url = "https://files.pythonhosted.org/packages/98/f0/38d6a12b44d4636afc189c1eb4c95c49bfe4184801dcc3a54ef15382abd5/mujoco-3.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de3d8d4c17b7831c4e1d760f9b19d4cf4c072014733489ed0a0eed32aad4348", size = 6283044, upload-time = "2025-02-27T19:15:47.815Z" },
+ { url = "https://files.pythonhosted.org/packages/57/92/7753403daa094fefcb8ce586a42170d79cd944c7eb466eb99d9220dec124/mujoco-3.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7955b8b0b9803fb41a1a800eb305fd26612cba3d5fa2beda2f886d9816283894", size = 6640054, upload-time = "2025-02-27T19:15:51.942Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/9f/48c8dad4b4edae9188e6bf5afdd614c73eb20f65bc9206be01c982564349/mujoco-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:7a0133c7a843e3ad9c48f436b09cd87df934421fbc751283f51eec79647d31e6", size = 4906759, upload-time = "2025-02-27T19:15:56.42Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "6.6.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/6b/86f353088c1358e76fd30b0146947fddecee812703b604ee901e85cd2a80/multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f", size = 77054, upload-time = "2025-08-11T12:06:02.99Z" },
+ { url = "https://files.pythonhosted.org/packages/19/5d/c01dc3d3788bb877bd7f5753ea6eb23c1beeca8044902a8f5bfb54430f63/multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb", size = 44914, upload-time = "2025-08-11T12:06:05.264Z" },
+ { url = "https://files.pythonhosted.org/packages/46/44/964dae19ea42f7d3e166474d8205f14bb811020e28bc423d46123ddda763/multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495", size = 44601, upload-time = "2025-08-11T12:06:06.627Z" },
+ { url = "https://files.pythonhosted.org/packages/31/20/0616348a1dfb36cb2ab33fc9521de1f27235a397bf3f59338e583afadd17/multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8", size = 224821, upload-time = "2025-08-11T12:06:08.06Z" },
+ { url = "https://files.pythonhosted.org/packages/14/26/5d8923c69c110ff51861af05bd27ca6783011b96725d59ccae6d9daeb627/multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7", size = 242608, upload-time = "2025-08-11T12:06:09.697Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/cc/e2ad3ba9459aa34fa65cf1f82a5c4a820a2ce615aacfb5143b8817f76504/multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796", size = 222324, upload-time = "2025-08-11T12:06:10.905Z" },
+ { url = "https://files.pythonhosted.org/packages/19/db/4ed0f65701afbc2cb0c140d2d02928bb0fe38dd044af76e58ad7c54fd21f/multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db", size = 253234, upload-time = "2025-08-11T12:06:12.658Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c1/5160c9813269e39ae14b73debb907bfaaa1beee1762da8c4fb95df4764ed/multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0", size = 251613, upload-time = "2025-08-11T12:06:13.97Z" },
+ { url = "https://files.pythonhosted.org/packages/05/a9/48d1bd111fc2f8fb98b2ed7f9a115c55a9355358432a19f53c0b74d8425d/multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877", size = 241649, upload-time = "2025-08-11T12:06:15.204Z" },
+ { url = "https://files.pythonhosted.org/packages/85/2a/f7d743df0019408768af8a70d2037546a2be7b81fbb65f040d76caafd4c5/multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace", size = 239238, upload-time = "2025-08-11T12:06:16.467Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/b8/4f4bb13323c2d647323f7919201493cf48ebe7ded971717bfb0f1a79b6bf/multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6", size = 233517, upload-time = "2025-08-11T12:06:18.107Z" },
+ { url = "https://files.pythonhosted.org/packages/33/29/4293c26029ebfbba4f574febd2ed01b6f619cfa0d2e344217d53eef34192/multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb", size = 243122, upload-time = "2025-08-11T12:06:19.361Z" },
+ { url = "https://files.pythonhosted.org/packages/20/60/a1c53628168aa22447bfde3a8730096ac28086704a0d8c590f3b63388d0c/multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb", size = 248992, upload-time = "2025-08-11T12:06:20.661Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3b/55443a0c372f33cae5d9ec37a6a973802884fa0ab3586659b197cf8cc5e9/multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987", size = 243708, upload-time = "2025-08-11T12:06:21.891Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/60/a18c6900086769312560b2626b18e8cca22d9e85b1186ba77f4755b11266/multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f", size = 237498, upload-time = "2025-08-11T12:06:23.206Z" },
+ { url = "https://files.pythonhosted.org/packages/11/3d/8bdd8bcaff2951ce2affccca107a404925a2beafedd5aef0b5e4a71120a6/multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f", size = 41415, upload-time = "2025-08-11T12:06:24.77Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/53/cab1ad80356a4cd1b685a254b680167059b433b573e53872fab245e9fc95/multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0", size = 46046, upload-time = "2025-08-11T12:06:25.893Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/9a/874212b6f5c1c2d870d0a7adc5bb4cfe9b0624fa15cdf5cf757c0f5087ae/multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729", size = 43147, upload-time = "2025-08-11T12:06:27.534Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c", size = 76472, upload-time = "2025-08-11T12:06:29.006Z" },
+ { url = "https://files.pythonhosted.org/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb", size = 44634, upload-time = "2025-08-11T12:06:30.374Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e", size = 44282, upload-time = "2025-08-11T12:06:31.958Z" },
+ { url = "https://files.pythonhosted.org/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded", size = 229696, upload-time = "2025-08-11T12:06:33.087Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683", size = 246665, upload-time = "2025-08-11T12:06:34.448Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a", size = 225485, upload-time = "2025-08-11T12:06:35.672Z" },
+ { url = "https://files.pythonhosted.org/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9", size = 257318, upload-time = "2025-08-11T12:06:36.98Z" },
+ { url = "https://files.pythonhosted.org/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50", size = 254689, upload-time = "2025-08-11T12:06:38.233Z" },
+ { url = "https://files.pythonhosted.org/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52", size = 246709, upload-time = "2025-08-11T12:06:39.517Z" },
+ { url = "https://files.pythonhosted.org/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6", size = 243185, upload-time = "2025-08-11T12:06:40.796Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e", size = 237838, upload-time = "2025-08-11T12:06:42.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3", size = 246368, upload-time = "2025-08-11T12:06:44.304Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c", size = 253339, upload-time = "2025-08-11T12:06:45.597Z" },
+ { url = "https://files.pythonhosted.org/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b", size = 246933, upload-time = "2025-08-11T12:06:46.841Z" },
+ { url = "https://files.pythonhosted.org/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f", size = 242225, upload-time = "2025-08-11T12:06:48.588Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2", size = 41306, upload-time = "2025-08-11T12:06:49.95Z" },
+ { url = "https://files.pythonhosted.org/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e", size = 46029, upload-time = "2025-08-11T12:06:51.082Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf", size = 43017, upload-time = "2025-08-11T12:06:52.243Z" },
+ { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516, upload-time = "2025-08-11T12:06:53.393Z" },
+ { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394, upload-time = "2025-08-11T12:06:54.555Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591, upload-time = "2025-08-11T12:06:55.672Z" },
+ { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215, upload-time = "2025-08-11T12:06:57.213Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299, upload-time = "2025-08-11T12:06:58.946Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357, upload-time = "2025-08-11T12:07:00.301Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369, upload-time = "2025-08-11T12:07:01.638Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341, upload-time = "2025-08-11T12:07:02.943Z" },
+ { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100, upload-time = "2025-08-11T12:07:04.564Z" },
+ { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584, upload-time = "2025-08-11T12:07:05.914Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018, upload-time = "2025-08-11T12:07:08.301Z" },
+ { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477, upload-time = "2025-08-11T12:07:10.248Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575, upload-time = "2025-08-11T12:07:11.928Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649, upload-time = "2025-08-11T12:07:13.244Z" },
+ { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505, upload-time = "2025-08-11T12:07:14.57Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888, upload-time = "2025-08-11T12:07:15.904Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072, upload-time = "2025-08-11T12:07:17.045Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222, upload-time = "2025-08-11T12:07:18.328Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848, upload-time = "2025-08-11T12:07:19.912Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060, upload-time = "2025-08-11T12:07:21.163Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269, upload-time = "2025-08-11T12:07:22.392Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158, upload-time = "2025-08-11T12:07:23.636Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076, upload-time = "2025-08-11T12:07:25.049Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694, upload-time = "2025-08-11T12:07:26.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350, upload-time = "2025-08-11T12:07:27.94Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250, upload-time = "2025-08-11T12:07:29.303Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900, upload-time = "2025-08-11T12:07:30.764Z" },
+ { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355, upload-time = "2025-08-11T12:07:32.205Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061, upload-time = "2025-08-11T12:07:33.623Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675, upload-time = "2025-08-11T12:07:34.958Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247, upload-time = "2025-08-11T12:07:36.588Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960, upload-time = "2025-08-11T12:07:39.735Z" },
+ { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078, upload-time = "2025-08-11T12:07:41.525Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708, upload-time = "2025-08-11T12:07:43.405Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912, upload-time = "2025-08-11T12:07:45.082Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076, upload-time = "2025-08-11T12:07:46.746Z" },
+ { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812, upload-time = "2025-08-11T12:07:48.402Z" },
+ { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313, upload-time = "2025-08-11T12:07:49.679Z" },
+ { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777, upload-time = "2025-08-11T12:07:51.318Z" },
+ { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321, upload-time = "2025-08-11T12:07:52.965Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954, upload-time = "2025-08-11T12:07:54.423Z" },
+ { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612, upload-time = "2025-08-11T12:07:55.914Z" },
+ { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528, upload-time = "2025-08-11T12:07:57.371Z" },
+ { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329, upload-time = "2025-08-11T12:07:58.844Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928, upload-time = "2025-08-11T12:08:01.037Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228, upload-time = "2025-08-11T12:08:02.96Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869, upload-time = "2025-08-11T12:08:04.746Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446, upload-time = "2025-08-11T12:08:06.332Z" },
+ { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299, upload-time = "2025-08-11T12:08:07.931Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926, upload-time = "2025-08-11T12:08:09.467Z" },
+ { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383, upload-time = "2025-08-11T12:08:10.981Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775, upload-time = "2025-08-11T12:08:12.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100, upload-time = "2025-08-11T12:08:13.823Z" },
+ { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501, upload-time = "2025-08-11T12:08:15.173Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" },
+]
+
+[[package]]
+name = "mypy"
+version = "1.18.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "pathspec" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/03/6f/657961a0743cff32e6c0611b63ff1c1970a0b482ace35b069203bf705187/mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c", size = 12807973, upload-time = "2025-09-19T00:10:35.282Z" },
+ { url = "https://files.pythonhosted.org/packages/10/e9/420822d4f661f13ca8900f5fa239b40ee3be8b62b32f3357df9a3045a08b/mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e", size = 11896527, upload-time = "2025-09-19T00:10:55.791Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/73/a05b2bbaa7005f4642fcfe40fb73f2b4fb6bb44229bd585b5878e9a87ef8/mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b", size = 12507004, upload-time = "2025-09-19T00:11:05.411Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/01/f6e4b9f0d031c11ccbd6f17da26564f3a0f3c4155af344006434b0a05a9d/mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66", size = 13245947, upload-time = "2025-09-19T00:10:46.923Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/97/19727e7499bfa1ae0773d06afd30ac66a58ed7437d940c70548634b24185/mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428", size = 13499217, upload-time = "2025-09-19T00:09:39.472Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/4f/90dc8c15c1441bf31cf0f9918bb077e452618708199e530f4cbd5cede6ff/mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed", size = 9766753, upload-time = "2025-09-19T00:10:49.161Z" },
+ { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" },
+ { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" },
+ { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" },
+ { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" },
+ { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" },
+ { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" },
+ { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" },
+ { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" },
+ { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" },
+ { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" },
+ { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
+]
+
+[[package]]
+name = "nmcli"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/57/a4b743fe2a06014236c081db3396572b4177896735504fc00ce42ff55a89/nmcli-1.5.0.tar.gz", hash = "sha256:6261dbd6f48b8454b7efc992f4285a5c126613a91a965d2c6c2caf749dec4ddf", size = 14994, upload-time = "2024-12-07T06:40:58.043Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/76/db262236cd1dbb18b1f98762ecba67cc97b572c74409224e61bbf1ac0c6a/nmcli-1.5.0-py3-none-any.whl", hash = "sha256:55e8cba0b4650f8d0741c9c403f8e33c6ad270b66bc23f9dc830214ef37b8429", size = 18909, upload-time = "2024-12-07T06:40:56.205Z" },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/b2/ce4b867d8cd9c0ee84938ae1e6a6f7926ebf928c9090d036fc3c6a04f946/numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", size = 20273920, upload-time = "2025-04-19T23:27:42.561Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/4e/3d9e6d16237c2aa5485695f0626cbba82f6481efca2e9132368dea3b885e/numpy-2.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f4a922da1729f4c40932b2af4fe84909c7a6e167e6e99f71838ce3a29f3fe26", size = 21252117, upload-time = "2025-04-19T22:31:01.142Z" },
+ { url = "https://files.pythonhosted.org/packages/38/e4/db91349d4079cd15c02ff3b4b8882a529991d6aca077db198a2f2a670406/numpy-2.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6f91524d31b34f4a5fee24f5bc16dcd1491b668798b6d85585d836c1e633a6a", size = 14424615, upload-time = "2025-04-19T22:31:24.873Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/59/6e5b011f553c37b008bd115c7ba7106a18f372588fbb1b430b7a5d2c41ce/numpy-2.2.5-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:19f4718c9012e3baea91a7dba661dcab2451cda2550678dc30d53acb91a7290f", size = 5428691, upload-time = "2025-04-19T22:31:33.998Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/58/d5d70ebdac82b3a6ddf409b3749ca5786636e50fd64d60edb46442af6838/numpy-2.2.5-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:eb7fd5b184e5d277afa9ec0ad5e4eb562ecff541e7f60e69ee69c8d59e9aeaba", size = 6965010, upload-time = "2025-04-19T22:31:45.281Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/a8/c290394be346d4e7b48a40baf292626fd96ec56a6398ace4c25d9079bc6a/numpy-2.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6413d48a9be53e183eb06495d8e3b006ef8f87c324af68241bbe7a39e8ff54c3", size = 14369885, upload-time = "2025-04-19T22:32:06.557Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/70/fed13c70aabe7049368553e81d7ca40f305f305800a007a956d7cd2e5476/numpy-2.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7451f92eddf8503c9b8aa4fe6aa7e87fd51a29c2cfc5f7dbd72efde6c65acf57", size = 16418372, upload-time = "2025-04-19T22:32:31.716Z" },
+ { url = "https://files.pythonhosted.org/packages/04/ab/c3c14f25ddaecd6fc58a34858f6a93a21eea6c266ba162fa99f3d0de12ac/numpy-2.2.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0bcb1d057b7571334139129b7f941588f69ce7c4ed15a9d6162b2ea54ded700c", size = 15883173, upload-time = "2025-04-19T22:32:55.106Z" },
+ { url = "https://files.pythonhosted.org/packages/50/18/f53710a19042911c7aca824afe97c203728a34b8cf123e2d94621a12edc3/numpy-2.2.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36ab5b23915887543441efd0417e6a3baa08634308894316f446027611b53bf1", size = 18206881, upload-time = "2025-04-19T22:33:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ec/5b407bab82f10c65af5a5fe754728df03f960fd44d27c036b61f7b3ef255/numpy-2.2.5-cp310-cp310-win32.whl", hash = "sha256:422cc684f17bc963da5f59a31530b3936f57c95a29743056ef7a7903a5dbdf88", size = 6609852, upload-time = "2025-04-19T22:33:33.357Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/f5/467ca8675c7e6c567f571d8db942cc10a87588bd9e20a909d8af4171edda/numpy-2.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:e4f0b035d9d0ed519c813ee23e0a733db81ec37d2e9503afbb6e54ccfdee0fa7", size = 12944922, upload-time = "2025-04-19T22:33:53.192Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/fb/e4e4c254ba40e8f0c78218f9e86304628c75b6900509b601c8433bdb5da7/numpy-2.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c42365005c7a6c42436a54d28c43fe0e01ca11eb2ac3cefe796c25a5f98e5e9b", size = 21256475, upload-time = "2025-04-19T22:34:24.174Z" },
+ { url = "https://files.pythonhosted.org/packages/81/32/dd1f7084f5c10b2caad778258fdaeedd7fbd8afcd2510672811e6138dfac/numpy-2.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:498815b96f67dc347e03b719ef49c772589fb74b8ee9ea2c37feae915ad6ebda", size = 14461474, upload-time = "2025-04-19T22:34:46.578Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/65/937cdf238ef6ac54ff749c0f66d9ee2b03646034c205cea9b6c51f2f3ad1/numpy-2.2.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6411f744f7f20081b1b4e7112e0f4c9c5b08f94b9f086e6f0adf3645f85d3a4d", size = 5426875, upload-time = "2025-04-19T22:34:56.281Z" },
+ { url = "https://files.pythonhosted.org/packages/25/17/814515fdd545b07306eaee552b65c765035ea302d17de1b9cb50852d2452/numpy-2.2.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9de6832228f617c9ef45d948ec1cd8949c482238d68b2477e6f642c33a7b0a54", size = 6969176, upload-time = "2025-04-19T22:35:07.518Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/32/a66db7a5c8b5301ec329ab36d0ecca23f5e18907f43dbd593c8ec326d57c/numpy-2.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:369e0d4647c17c9363244f3468f2227d557a74b6781cb62ce57cf3ef5cc7c610", size = 14374850, upload-time = "2025-04-19T22:35:31.347Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/c9/1bf6ada582eebcbe8978f5feb26584cd2b39f94ededeea034ca8f84af8c8/numpy-2.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262d23f383170f99cd9191a7c85b9a50970fe9069b2f8ab5d786eca8a675d60b", size = 16430306, upload-time = "2025-04-19T22:35:57.573Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/f0/3f741863f29e128f4fcfdb99253cc971406b402b4584663710ee07f5f7eb/numpy-2.2.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa70fdbdc3b169d69e8c59e65c07a1c9351ceb438e627f0fdcd471015cd956be", size = 15884767, upload-time = "2025-04-19T22:36:22.245Z" },
+ { url = "https://files.pythonhosted.org/packages/98/d9/4ccd8fd6410f7bf2d312cbc98892e0e43c2fcdd1deae293aeb0a93b18071/numpy-2.2.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37e32e985f03c06206582a7323ef926b4e78bdaa6915095ef08070471865b906", size = 18219515, upload-time = "2025-04-19T22:36:49.822Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/56/783237243d4395c6dd741cf16eeb1a9035ee3d4310900e6b17e875d1b201/numpy-2.2.5-cp311-cp311-win32.whl", hash = "sha256:f5045039100ed58fa817a6227a356240ea1b9a1bc141018864c306c1a16d4175", size = 6607842, upload-time = "2025-04-19T22:37:01.624Z" },
+ { url = "https://files.pythonhosted.org/packages/98/89/0c93baaf0094bdaaaa0536fe61a27b1dce8a505fa262a865ec142208cfe9/numpy-2.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:b13f04968b46ad705f7c8a80122a42ae8f620536ea38cf4bdd374302926424dd", size = 12949071, upload-time = "2025-04-19T22:37:21.098Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/f7/1fd4ff108cd9d7ef929b8882692e23665dc9c23feecafbb9c6b80f4ec583/numpy-2.2.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051", size = 20948633, upload-time = "2025-04-19T22:37:52.4Z" },
+ { url = "https://files.pythonhosted.org/packages/12/03/d443c278348371b20d830af155ff2079acad6a9e60279fac2b41dbbb73d8/numpy-2.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc", size = 14176123, upload-time = "2025-04-19T22:38:15.058Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0b/5ca264641d0e7b14393313304da48b225d15d471250376f3fbdb1a2be603/numpy-2.2.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e", size = 5163817, upload-time = "2025-04-19T22:38:24.885Z" },
+ { url = "https://files.pythonhosted.org/packages/04/b3/d522672b9e3d28e26e1613de7675b441bbd1eaca75db95680635dd158c67/numpy-2.2.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa", size = 6698066, upload-time = "2025-04-19T22:38:35.782Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/93/0f7a75c1ff02d4b76df35079676b3b2719fcdfb39abdf44c8b33f43ef37d/numpy-2.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571", size = 14087277, upload-time = "2025-04-19T22:38:57.697Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/d9/7c338b923c53d431bc837b5b787052fef9ae68a56fe91e325aac0d48226e/numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073", size = 16135742, upload-time = "2025-04-19T22:39:22.689Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/10/4dec9184a5d74ba9867c6f7d1e9f2e0fb5fe96ff2bf50bb6f342d64f2003/numpy-2.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8", size = 15581825, upload-time = "2025-04-19T22:39:45.794Z" },
+ { url = "https://files.pythonhosted.org/packages/80/1f/2b6fcd636e848053f5b57712a7d1880b1565eec35a637fdfd0a30d5e738d/numpy-2.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae", size = 17899600, upload-time = "2025-04-19T22:40:13.427Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/87/36801f4dc2623d76a0a3835975524a84bd2b18fe0f8835d45c8eae2f9ff2/numpy-2.2.5-cp312-cp312-win32.whl", hash = "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb", size = 6312626, upload-time = "2025-04-19T22:40:25.223Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/09/4ffb4d6cfe7ca6707336187951992bd8a8b9142cf345d87ab858d2d7636a/numpy-2.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282", size = 12645715, upload-time = "2025-04-19T22:40:44.528Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a0/0aa7f0f4509a2e07bd7a509042967c2fab635690d4f48c6c7b3afd4f448c/numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", size = 20935102, upload-time = "2025-04-19T22:41:16.234Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/e4/a6a9f4537542912ec513185396fce52cdd45bdcf3e9d921ab02a93ca5aa9/numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", size = 14191709, upload-time = "2025-04-19T22:41:38.472Z" },
+ { url = "https://files.pythonhosted.org/packages/be/65/72f3186b6050bbfe9c43cb81f9df59ae63603491d36179cf7a7c8d216758/numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", size = 5149173, upload-time = "2025-04-19T22:41:47.823Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/e9/83e7a9432378dde5802651307ae5e9ea07bb72b416728202218cd4da2801/numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", size = 6684502, upload-time = "2025-04-19T22:41:58.689Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/27/b80da6c762394c8ee516b74c1f686fcd16c8f23b14de57ba0cad7349d1d2/numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", size = 14084417, upload-time = "2025-04-19T22:42:19.897Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", size = 16133807, upload-time = "2025-04-19T22:42:44.433Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/9b/4cc171a0acbe4666f7775cfd21d4eb6bb1d36d3a0431f48a73e9212d2278/numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", size = 15575611, upload-time = "2025-04-19T22:43:09.928Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/45/40f4135341850df48f8edcf949cf47b523c404b712774f8855a64c96ef29/numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", size = 17895747, upload-time = "2025-04-19T22:43:36.983Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/4c/b32a17a46f0ffbde8cc82df6d3daeaf4f552e346df143e1b188a701a8f09/numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", size = 6309594, upload-time = "2025-04-19T22:47:10.523Z" },
+ { url = "https://files.pythonhosted.org/packages/13/ae/72e6276feb9ef06787365b05915bfdb057d01fceb4a43cb80978e518d79b/numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", size = 12638356, upload-time = "2025-04-19T22:47:30.253Z" },
+ { url = "https://files.pythonhosted.org/packages/79/56/be8b85a9f2adb688e7ded6324e20149a03541d2b3297c3ffc1a73f46dedb/numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", size = 20963778, upload-time = "2025-04-19T22:44:09.251Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/77/19c5e62d55bff507a18c3cdff82e94fe174957bad25860a991cac719d3ab/numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", size = 14207279, upload-time = "2025-04-19T22:44:31.383Z" },
+ { url = "https://files.pythonhosted.org/packages/75/22/aa11f22dc11ff4ffe4e849d9b63bbe8d4ac6d5fae85ddaa67dfe43be3e76/numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", size = 5199247, upload-time = "2025-04-19T22:44:40.361Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/6c/12d5e760fc62c08eded0394f62039f5a9857f758312bf01632a81d841459/numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", size = 6711087, upload-time = "2025-04-19T22:44:51.188Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/94/ece8280cf4218b2bee5cec9567629e61e51b4be501e5c6840ceb593db945/numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", size = 14059964, upload-time = "2025-04-19T22:45:12.451Z" },
+ { url = "https://files.pythonhosted.org/packages/39/41/c5377dac0514aaeec69115830a39d905b1882819c8e65d97fc60e177e19e/numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", size = 16121214, upload-time = "2025-04-19T22:45:37.734Z" },
+ { url = "https://files.pythonhosted.org/packages/db/54/3b9f89a943257bc8e187145c6bc0eb8e3d615655f7b14e9b490b053e8149/numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", size = 15575788, upload-time = "2025-04-19T22:46:01.908Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/c4/2e407e85df35b29f79945751b8f8e671057a13a376497d7fb2151ba0d290/numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", size = 17893672, upload-time = "2025-04-19T22:46:28.585Z" },
+ { url = "https://files.pythonhosted.org/packages/29/7e/d0b44e129d038dba453f00d0e29ebd6eaf2f06055d72b95b9947998aca14/numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", size = 6377102, upload-time = "2025-04-19T22:46:39.949Z" },
+ { url = "https://files.pythonhosted.org/packages/63/be/b85e4aa4bf42c6502851b971f1c326d583fcc68227385f92089cf50a7b45/numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", size = 12750096, upload-time = "2025-04-19T22:47:00.147Z" },
+ { url = "https://files.pythonhosted.org/packages/35/e4/5ef5ef1d4308f96961198b2323bfc7c7afb0ccc0d623b01c79bc87ab496d/numpy-2.2.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4ea7e1cff6784e58fe281ce7e7f05036b3e1c89c6f922a6bfbc0a7e8768adbe", size = 21083404, upload-time = "2025-04-19T22:48:01.605Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/5f/bde9238e8e977652a16a4b114ed8aa8bb093d718c706eeecb5f7bfa59572/numpy-2.2.5-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d7543263084a85fbc09c704b515395398d31d6395518446237eac219eab9e55e", size = 6828578, upload-time = "2025-04-19T22:48:13.118Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/7f/813f51ed86e559ab2afb6a6f33aa6baf8a560097e25e4882a938986c76c2/numpy-2.2.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0255732338c4fdd00996c0421884ea8a3651eea555c3a56b84892b66f696eb70", size = 16234796, upload-time = "2025-04-19T22:48:37.102Z" },
+ { url = "https://files.pythonhosted.org/packages/68/67/1175790323026d3337cc285cc9c50eca637d70472b5e622529df74bb8f37/numpy-2.2.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169", size = 12859001, upload-time = "2025-04-19T22:48:57.665Z" },
+]
+
+[[package]]
+name = "numpy-stl"
+version = "3.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "python-utils" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/80/96366d1b69223d4ca55fca30f158096df169b00aa6b4dd963025980e7ede/numpy_stl-3.2.0.tar.gz", hash = "sha256:5a20c3f79cddaa0abc6a4b99f5486aceed4f88152f29b19a57acc844e183fd4d", size = 824382, upload-time = "2024-11-25T17:15:43.227Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ea/2c/e17b8814050427929077639d35a42187a006922600d4840475bdc5f64ebb/numpy_stl-3.2.0-py3-none-any.whl", hash = "sha256:697c81b107231362460aaedf4647e81ba54f54f59c896772fea7904c9c439da5", size = 20775, upload-time = "2024-11-25T17:15:41.346Z" },
+]
+
+[[package]]
+name = "onnxruntime"
+version = "1.22.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coloredlogs" },
+ { name = "flatbuffers" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "protobuf" },
+ { name = "sympy" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/b9/664a1ffee62fa51529fac27b37409d5d28cadee8d97db806fcba68339b7e/onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95", size = 34319864, upload-time = "2025-07-10T19:15:15.371Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/64/bc7221e92c994931024e22b22401b962c299e991558c3d57f7e34538b4b9/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73", size = 14472246, upload-time = "2025-07-10T19:15:19.403Z" },
+ { url = "https://files.pythonhosted.org/packages/84/57/901eddbfb59ac4d008822b236450d5765cafcd450c787019416f8d3baf11/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bddc75868bcf6f9ed76858a632f65f7b1846bdcefc6d637b1e359c2c68609964", size = 16459905, upload-time = "2025-07-10T19:15:21.749Z" },
+ { url = "https://files.pythonhosted.org/packages/de/90/d6a1eb9b47e66a18afe7d1cf7cf0b2ef966ffa6f44d9f32d94c2be2860fb/onnxruntime-1.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:01e2f21b2793eb0c8642d2be3cee34cc7d96b85f45f6615e4e220424158877ce", size = 12689001, upload-time = "2025-07-10T19:15:23.848Z" },
+ { url = "https://files.pythonhosted.org/packages/82/ff/4a1a6747e039ef29a8d4ee4510060e9a805982b6da906a3da2306b7a3be6/onnxruntime-1.22.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:f4581bccb786da68725d8eac7c63a8f31a89116b8761ff8b4989dc58b61d49a0", size = 34324148, upload-time = "2025-07-10T19:15:26.584Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/05/9f1929723f1cca8c9fb1b2b97ac54ce61362c7201434d38053ea36ee4225/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae7526cf10f93454beb0f751e78e5cb7619e3b92f9fc3bd51aa6f3b7a8977e5", size = 14473779, upload-time = "2025-07-10T19:15:30.183Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f3/c93eb4167d4f36ea947930f82850231f7ce0900cb00e1a53dc4995b60479/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6effa1299ac549a05c784d50292e3378dbbf010346ded67400193b09ddc2f04", size = 16460799, upload-time = "2025-07-10T19:15:33.005Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/01/e536397b03e4462d3260aee5387e6f606c8fa9d2b20b1728f988c3c72891/onnxruntime-1.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:f28a42bb322b4ca6d255531bb334a2b3e21f172e37c1741bd5e66bc4b7b61f03", size = 12689881, upload-time = "2025-07-10T19:15:35.501Z" },
+ { url = "https://files.pythonhosted.org/packages/48/70/ca2a4d38a5deccd98caa145581becb20c53684f451e89eb3a39915620066/onnxruntime-1.22.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:a938d11c0dc811badf78e435daa3899d9af38abee950d87f3ab7430eb5b3cf5a", size = 34342883, upload-time = "2025-07-10T19:15:38.223Z" },
+ { url = "https://files.pythonhosted.org/packages/29/e5/00b099b4d4f6223b610421080d0eed9327ef9986785c9141819bbba0d396/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984cea2a02fcc5dfea44ade9aca9fe0f7a8a2cd6f77c258fc4388238618f3928", size = 14473861, upload-time = "2025-07-10T19:15:42.911Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/50/519828a5292a6ccd8d5cd6d2f72c6b36ea528a2ef68eca69647732539ffa/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d39a530aff1ec8d02e365f35e503193991417788641b184f5b1e8c9a6d5ce8d", size = 16475713, upload-time = "2025-07-10T19:15:45.452Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/54/7139d463bb0a312890c9a5db87d7815d4a8cce9e6f5f28d04f0b55fcb160/onnxruntime-1.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:6a64291d57ea966a245f749eb970f4fa05a64d26672e05a83fdb5db6b7d62f87", size = 12690910, upload-time = "2025-07-10T19:15:47.478Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/39/77cefa829740bd830915095d8408dce6d731b244e24b1f64fe3df9f18e86/onnxruntime-1.22.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:d29c7d87b6cbed8fecfd09dca471832384d12a69e1ab873e5effbb94adc3e966", size = 34342026, upload-time = "2025-07-10T19:15:50.266Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/a6/444291524cb52875b5de980a6e918072514df63a57a7120bf9dfae3aeed1/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:460487d83b7056ba98f1f7bac80287224c31d8149b15712b0d6f5078fcc33d0f", size = 14474014, upload-time = "2025-07-10T19:15:53.991Z" },
+ { url = "https://files.pythonhosted.org/packages/87/9d/45a995437879c18beff26eacc2322f4227224d04c6ac3254dce2e8950190/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0c37070268ba4e02a1a9d28560cd00cd1e94f0d4f275cbef283854f861a65fa", size = 16475427, upload-time = "2025-07-10T19:15:56.067Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/06/9c765e66ad32a7e709ce4cb6b95d7eaa9cb4d92a6e11ea97c20ffecaf765/onnxruntime-1.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:70980d729145a36a05f74b573435531f55ef9503bcda81fc6c3d6b9306199982", size = 12690841, upload-time = "2025-07-10T19:15:58.337Z" },
+ { url = "https://files.pythonhosted.org/packages/52/8c/02af24ee1c8dce4e6c14a1642a7a56cebe323d2fa01d9a360a638f7e4b75/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a7980bbc4b7f446bac26c3785652fe8730ed02617d765399e89ac7d44e0f7d", size = 14479333, upload-time = "2025-07-10T19:16:00.544Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/15/d75fd66aba116ce3732bb1050401394c5ec52074c4f7ee18db8838dd4667/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7e823624b015ea879d976cbef8bfaed2f7e2cc233d7506860a76dd37f8f381", size = 16477261, upload-time = "2025-07-10T19:16:03.226Z" },
+]
+
+[[package]]
+name = "onshape-to-robot"
+version = "1.7.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+ { name = "commentjson" },
+ { name = "numpy" },
+ { name = "numpy-stl" },
+ { name = "python-dotenv" },
+ { name = "requests" },
+ { name = "transforms3d" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/99/be262505d2a41a0bb7267c3037b4a4ed58460cf0f84408aa23f998d02c7f/onshape_to_robot-1.7.6.tar.gz", hash = "sha256:71aa33710f58ddba566fd6f2b17b02506794eb9c5032f4adbae34ce7cf168780", size = 48227, upload-time = "2025-06-17T22:09:32.856Z" }
+
+[[package]]
+name = "opencv-python"
+version = "4.12.0.88"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/71/25c98e634b6bdeca4727c7f6d6927b056080668c5008ad3c8fc9e7f8f6ec/opencv-python-4.12.0.88.tar.gz", hash = "sha256:8b738389cede219405f6f3880b851efa3415ccd674752219377353f017d2994d", size = 95373294, upload-time = "2025-07-07T09:20:52.389Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/85/68/3da40142e7c21e9b1d4e7ddd6c58738feb013203e6e4b803d62cdd9eb96b/opencv_python-4.12.0.88-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:f9a1f08883257b95a5764bf517a32d75aec325319c8ed0f89739a57fae9e92a5", size = 37877727, upload-time = "2025-07-07T09:13:31.47Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7c/042abe49f58d6ee7e1028eefc3334d98ca69b030e3b567fe245a2b28ea6f/opencv_python-4.12.0.88-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:812eb116ad2b4de43ee116fcd8991c3a687f099ada0b04e68f64899c09448e81", size = 57326471, upload-time = "2025-07-07T09:13:41.26Z" },
+ { url = "https://files.pythonhosted.org/packages/62/3a/440bd64736cf8116f01f3b7f9f2e111afb2e02beb2ccc08a6458114a6b5d/opencv_python-4.12.0.88-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:51fd981c7df6af3e8f70b1556696b05224c4e6b6777bdd2a46b3d4fb09de1a92", size = 45887139, upload-time = "2025-07-07T09:13:50.761Z" },
+ { url = "https://files.pythonhosted.org/packages/68/1f/795e7f4aa2eacc59afa4fb61a2e35e510d06414dd5a802b51a012d691b37/opencv_python-4.12.0.88-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:092c16da4c5a163a818f120c22c5e4a2f96e0db4f24e659c701f1fe629a690f9", size = 67041680, upload-time = "2025-07-07T09:14:01.995Z" },
+ { url = "https://files.pythonhosted.org/packages/02/96/213fea371d3cb2f1d537612a105792aa0a6659fb2665b22cad709a75bd94/opencv_python-4.12.0.88-cp37-abi3-win32.whl", hash = "sha256:ff554d3f725b39878ac6a2e1fa232ec509c36130927afc18a1719ebf4fbf4357", size = 30284131, upload-time = "2025-07-07T09:14:08.819Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/80/eb88edc2e2b11cd2dd2e56f1c80b5784d11d6e6b7f04a1145df64df40065/opencv_python-4.12.0.88-cp37-abi3-win_amd64.whl", hash = "sha256:d98edb20aa932fd8ebd276a72627dad9dc097695b3d435a4257557bbb49a79d2", size = 39000307, upload-time = "2025-07-07T09:14:16.641Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
+[[package]]
+name = "parso"
+version = "0.8.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
+]
+
+[[package]]
+name = "pexpect"
+version = "4.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ptyprocess" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
+]
+
+[[package]]
+name = "pillow"
+version = "11.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" },
+ { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" },
+ { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" },
+ { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" },
+ { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" },
+ { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" },
+ { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" },
+ { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" },
+ { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" },
+ { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" },
+ { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" },
+ { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" },
+ { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" },
+ { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" },
+ { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" },
+ { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" },
+ { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" },
+ { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" },
+ { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" },
+ { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" },
+ { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" },
+ { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" },
+ { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" },
+ { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" },
+ { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" },
+ { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" },
+ { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" },
+ { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" },
+ { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" },
+ { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" },
+]
+
+[[package]]
+name = "pin"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "cmeel-boost" },
+ { name = "cmeel-urdfdom" },
+ { name = "coal-library" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4c/00/0e2b8344ea7af3185909ddc91876abe0274d5b1257caf191414732495c9d/pin-3.4.0.tar.gz", hash = "sha256:95c8e9c707dad9c412247874ca3f8ac77f0d5f51e49f02856f88146478e2660b", size = 99748596, upload-time = "2025-02-13T01:08:55.83Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/4f/29ec215bab0de9c5b017ad4ad81aca8ba2dbba9d7bcefb38ce18dd1871f6/pin-3.4.0-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e3ea1fa363908c8ac9f541bd3c136cb02d720fe3e85760459bc0f9152a9018ea", size = 14580514, upload-time = "2025-02-13T01:07:18.987Z" },
+ { url = "https://files.pythonhosted.org/packages/64/58/b04a646078c8fba139e8b33314e1b28e5b0e6f00844fee72d25933553d66/pin-3.4.0-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c5cc929c8c8849c20a00410426aad002fdf44af9467d077962d75332d2097ab", size = 13218379, upload-time = "2025-02-13T01:07:22.872Z" },
+ { url = "https://files.pythonhosted.org/packages/03/fc/d15717bc94a92a561b18834e392b1dced81420dfaf08f197fa6cb8eba205/pin-3.4.0-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ace2c10302fccd3d3e9dc7214b6b9e307cb88df84ebe365e46462e71ef3857ae", size = 17434017, upload-time = "2025-02-13T01:07:26.018Z" },
+ { url = "https://files.pythonhosted.org/packages/08/89/bba3b9277ab96dc922f0b901befde0e53392e914754942d5ce1a1ef53d97/pin-3.4.0-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f0feb5a2325e9156e790d4f39c55cab812937e87c6d73a18f027d4afccc4b997", size = 17813130, upload-time = "2025-02-13T01:07:32.019Z" },
+ { url = "https://files.pythonhosted.org/packages/17/7b/5e4e451a5c1106a651009cecaa469408bfec5962be9890aef8a4c6f8efdf/pin-3.4.0-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:07f06f104c503aa72c134eac308c07ecc704c5f47dc5e4c6edaa31f9b42e5a28", size = 14580686, upload-time = "2025-02-13T01:07:36.383Z" },
+ { url = "https://files.pythonhosted.org/packages/34/d7/549c1439d4ec7a505f6a4464743263868c3f2018daebc838af7976291d35/pin-3.4.0-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:88e2fac5896c23f76e5d5fa49f023581baa9e703bfea26a0bb72750d07313d78", size = 13217948, upload-time = "2025-02-13T01:07:40.473Z" },
+ { url = "https://files.pythonhosted.org/packages/18/8a/dc016fa16ae73d2d9fd3828bb6ee654479c155d644356523ffc0f0e2f749/pin-3.4.0-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b041f7ef2ef18a4be7cb228662cf0d7061edd2ddc5cf30b54f1344dc0c491d38", size = 17432538, upload-time = "2025-02-13T01:07:43.516Z" },
+ { url = "https://files.pythonhosted.org/packages/12/a0/df562a0e531531fc13db2e18d1b71691fc5f8ae174e026b38b4c771f4dc4/pin-3.4.0-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8987a784028ade6145c0b5844a20accbb63bc8501f8d7260d67df6a0228678f1", size = 17812580, upload-time = "2025-02-13T01:07:47.452Z" },
+ { url = "https://files.pythonhosted.org/packages/47/07/bd33ac55ed1ef54b0301c939d24b32dd3194a72bf3a6e3be2de815cba21e/pin-3.4.0-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6a20fc936311039698cac062db89e48c5494c4953a989372b7981d741844324a", size = 14653652, upload-time = "2025-02-13T01:07:51.617Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/ab/ead22716b69fd6a703c7d81b325c3f20a1af44cdbf5d0e9295a7ffc8a5ba/pin-3.4.0-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5abd2c19a6bb63617aa951c722cb3bce7b633bca119a541baab1123a8d48c32", size = 13265144, upload-time = "2025-02-13T01:07:55.375Z" },
+ { url = "https://files.pythonhosted.org/packages/59/af/c3ec76c2ba38ae4d419a091c34fd7976ea4f1ee3f36baf6242abb21a19e5/pin-3.4.0-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e0547ea1665e8477968a22791216bcd3b70cd0c1c0091d417564ffabd1bb435e", size = 17298971, upload-time = "2025-02-13T01:07:58.72Z" },
+ { url = "https://files.pythonhosted.org/packages/db/a4/72ba496b491dc0892af2bb9c98bf615c6ff2ea9690a63f1154f906b960f3/pin-3.4.0-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:37941641077550264ef8e0f8a529f368414296e27b9f649467e185ab05445960", size = 17711678, upload-time = "2025-02-13T01:08:02.631Z" },
+ { url = "https://files.pythonhosted.org/packages/03/3b/1f7021c8c81098dcef3dafaee8f0eae25520a9ea5277d15dff58e55f9796/pin-3.4.0-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:964e055b147afb1705263c8ea1a9c574a2babd92d3b637f5255de4abbbdd4970", size = 14653665, upload-time = "2025-02-13T01:08:06.704Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e2/7a05c42df807b2070682cc2c1e86dce2964e6ef443bb385fca943a9c63ef/pin-3.4.0-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c35af977fb85878ad6281b9b2d06bcfbcf353cd8b1a0f97342de55f2dd3ab70f", size = 13265140, upload-time = "2025-02-13T01:08:09.674Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/02/022f77ea0433f7a7153a711681a8b5520ba453b5e56c904274df30540f59/pin-3.4.0-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ba80feba64744da94d41ef64e430aeaf358085647061b90b57641d0108326363", size = 17298972, upload-time = "2025-02-13T01:08:14.036Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/6a/87ffcfb152f24f8e8e3b0f2b4435f23b70786589f86fc2bb3f03d36c3fa4/pin-3.4.0-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:66534969e155ac4bbdd9b96e96b076ef45d4d46d98e0c12ea9468d738f7b7ea2", size = 17711687, upload-time = "2025-02-13T01:08:20.651Z" },
+]
+
+[[package]]
+name = "pip"
+version = "25.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" },
+]
+
+[[package]]
+name = "placo"
+version = "0.9.14"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+ { name = "eiquadprog" },
+ { name = "ischedule" },
+ { name = "meshcat" },
+ { name = "pin" },
+ { name = "rhoban-cmeel-jsoncpp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/45/293c9afc05a018bd2e2d6ec88a44958f09724686c826f576b04775d0d19b/placo-0.9.14.tar.gz", hash = "sha256:2ac04ede402a60fdfff240bd06dbb01fcd7b291bdb7b2d1d5ef595e7ca581b74", size = 136457, upload-time = "2025-07-07T23:43:26.547Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/49/16f21051e19d8adc1bdc1393db05f08bde7663201376ff644b5619b5ea6c/placo-0.9.14-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ed75678b1c0ca4c0cf614a16a60684247236f64b51781ae445eb941f96c74f79", size = 1684593, upload-time = "2025-07-07T23:42:45.863Z" },
+ { url = "https://files.pythonhosted.org/packages/19/10/c77194ac946a6ebb0a283bc43e76758a9edf082c68a33b9b2d8c6e2e5362/placo-0.9.14-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f2331d03d15dbdbf9a648f98b9434d359737367945e43bd8d8172c10fdbca899", size = 1549964, upload-time = "2025-07-07T23:42:47.965Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c5/e13748b59c751ae2e49edf28e8c97728714a7f1972bcb07a3d2d1976109f/placo-0.9.14-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a182ff80ed09156d3ed6bd16c15163a44a67e4ab982df4644528703eae0b39f5", size = 2112464, upload-time = "2025-07-07T23:42:49.959Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/a9/78e031655a661a5f835387ecf9314b0965daffbcbb02c3f1a9e190fd887b/placo-0.9.14-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:eb183ec0ec89c186e9b1cef7ee70878a27b0d5567d7673708a34753d6620ccab", size = 2177921, upload-time = "2025-07-07T23:42:51.462Z" },
+ { url = "https://files.pythonhosted.org/packages/96/da/d10aaa6e9f596f61fcd08d17ccd58f20572f4361150b0ff557153e510e19/placo-0.9.14-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1732cdada774c8a9dab70f5f8e8c361d8d2c62928d3b8d103811b2a2319bf1cd", size = 1684579, upload-time = "2025-07-07T23:42:54.211Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/94/8650034a283e2ca9b4b3eb95539368cd83d6bb624ed6f08cf4d7badb7345/placo-0.9.14-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b870f0afe186776438d3f89df4e78618c077a771fa76250026964db67e125f7f", size = 1549950, upload-time = "2025-07-07T23:42:55.765Z" },
+ { url = "https://files.pythonhosted.org/packages/33/f9/becef66e3b712cd8613b1132cb2337b53c8ea3714b550cfe558f80dc34a9/placo-0.9.14-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:97f12f6f29608d301a92c108a948ed9e2a40ca12b5f046969572cdba13e67d52", size = 2111904, upload-time = "2025-07-07T23:42:57.609Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/4f/3ec87720b6971af0430de17735666beabae6374066281dc71d908f049f56/placo-0.9.14-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:878a39f4f45e728785891271a332a26181a0f7fa57a825dc7ac239f35ab4c091", size = 2178050, upload-time = "2025-07-07T23:42:59.123Z" },
+ { url = "https://files.pythonhosted.org/packages/95/a8/03f3ab9d44b6aa754b652f1d652dd65ec94a4d3ec0432720d8b88a1605fe/placo-0.9.14-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e41150e03a303f4c90f8a6138330caca21d520160021b7b9e14b90e2dbbcaf58", size = 1705653, upload-time = "2025-07-07T23:43:00.836Z" },
+ { url = "https://files.pythonhosted.org/packages/14/39/e892d5c1ad42302490846dc5e6e3881d9d335bbeac18be1415b3ac4be58e/placo-0.9.14-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03c1fe072f7d0075ab2a4ea85b3fb62fedf7e2c4d9613f96a156a576d29255e2", size = 1564825, upload-time = "2025-07-07T23:43:02.724Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/70/d5a0a079770a8cc4eaa977aa7438ff7861ce2227d0949ea1b5b3651caf0b/placo-0.9.14-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ad96c8f3b5b0cf757048daed3e57b83765c402a27e5608a28496ed12cfad9692", size = 2092058, upload-time = "2025-07-07T23:43:04.109Z" },
+ { url = "https://files.pythonhosted.org/packages/27/9f/a80f6ffed0bf7b383aea8ae94668bc7d0046f5909692c0af8c0e10ecc9d1/placo-0.9.14-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:84ed219527c8868b4f520080c97280183ff311428d0e97225c53de8822c3783e", size = 2163101, upload-time = "2025-07-07T23:43:05.649Z" },
+ { url = "https://files.pythonhosted.org/packages/09/d6/14fb3749b99be2ccd7d2329df2a20ab7709ce7245dc7930c65babafd6dd6/placo-0.9.14-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3d0d4638f685591d8263c2fb66861255816784b995ba2ba9d7cc6882d957e4cb", size = 1705647, upload-time = "2025-07-07T23:43:07.092Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/2e/be6081a1d2aec95e7436700f5e8b008328e5ae2390b07ad650e84687e32c/placo-0.9.14-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:460a211febf9310074407e59ba3c8be52289148cc4f86868949ba9c462f381d2", size = 1564819, upload-time = "2025-07-07T23:43:08.961Z" },
+ { url = "https://files.pythonhosted.org/packages/63/43/b18d82fa0237e0ec6598a7f47f0ffbc007b9c3913d8d76099cffcf9d5de1/placo-0.9.14-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7b57831c2db2e808fd63c807149699c312d3ae09e1d6e10c9df3d12b2c66dee7", size = 2092073, upload-time = "2025-07-07T23:43:10.437Z" },
+ { url = "https://files.pythonhosted.org/packages/72/a1/d92bf06980fb4c265394e797751059fdb7256ef425e6f8399697172374f4/placo-0.9.14-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1d56205d66dd49672de18c23ef8e8fe6e5321b3d88ca4844e7117a47acaf9ce1", size = 2163105, upload-time = "2025-07-07T23:43:12.502Z" },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "pollen-bmi088-imu-library"
+version = "1.0.0rc1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ahrs" },
+ { name = "numpy" },
+ { name = "smbus3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/64/ca/f74143ec89fbc502c7d9fc6a4d20a7f56fad822389716daee5e2bfe9e364/pollen_bmi088_imu_library-1.0.0rc1.tar.gz", hash = "sha256:6a82eafa448807a87c7a9e1ed8ef395b0ce5f2f57fa536b5b3a3f428e72d8fcf", size = 3273, upload-time = "2025-10-30T08:57:43.422Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/0a/86991b5db3acb88bbe83ba1e586c1c401830c8dc3b95cb6e0637e60cd26d/pollen_bmi088_imu_library-1.0.0rc1-py3-none-any.whl", hash = "sha256:d13e7297a3042879c36b48f78c6d0e8426af2c5ca31efe312003c7cf74b657c8", size = 3748, upload-time = "2025-10-30T08:57:42.136Z" },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" },
+]
+
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.52"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "wcwidth" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
+]
+
+[[package]]
+name = "propcache"
+version = "0.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" },
+ { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" },
+ { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" },
+ { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" },
+ { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" },
+ { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" },
+ { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" },
+ { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" },
+ { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" },
+ { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" },
+ { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" },
+ { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" },
+ { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" },
+ { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" },
+ { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" },
+ { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" },
+ { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" },
+ { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" },
+ { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" },
+ { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" },
+ { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" },
+ { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" },
+ { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" },
+ { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
+]
+
+[[package]]
+name = "protobuf"
+version = "6.32.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" },
+ { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" },
+]
+
+[[package]]
+name = "psutil"
+version = "7.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" },
+ { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" },
+ { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" },
+]
+
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
+]
+
+[[package]]
+name = "pure-eval"
+version = "0.2.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
+]
+
+[[package]]
+name = "pyarrow"
+version = "21.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487, upload-time = "2025-07-18T00:57:31.761Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/d9/110de31880016e2afc52d8580b397dbe47615defbf09ca8cf55f56c62165/pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26", size = 31196837, upload-time = "2025-07-18T00:54:34.755Z" },
+ { url = "https://files.pythonhosted.org/packages/df/5f/c1c1997613abf24fceb087e79432d24c19bc6f7259cab57c2c8e5e545fab/pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79", size = 32659470, upload-time = "2025-07-18T00:54:38.329Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ed/b1589a777816ee33ba123ba1e4f8f02243a844fed0deec97bde9fb21a5cf/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb", size = 41055619, upload-time = "2025-07-18T00:54:42.172Z" },
+ { url = "https://files.pythonhosted.org/packages/44/28/b6672962639e85dc0ac36f71ab3a8f5f38e01b51343d7aa372a6b56fa3f3/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51", size = 42733488, upload-time = "2025-07-18T00:54:47.132Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/cc/de02c3614874b9089c94eac093f90ca5dfa6d5afe45de3ba847fd950fdf1/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a", size = 43329159, upload-time = "2025-07-18T00:54:51.686Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/3e/99473332ac40278f196e105ce30b79ab8affab12f6194802f2593d6b0be2/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594", size = 45050567, upload-time = "2025-07-18T00:54:56.679Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/f5/c372ef60593d713e8bfbb7e0c743501605f0ad00719146dc075faf11172b/pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634", size = 26217959, upload-time = "2025-07-18T00:55:00.482Z" },
+ { url = "https://files.pythonhosted.org/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b", size = 31243234, upload-time = "2025-07-18T00:55:03.812Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10", size = 32714370, upload-time = "2025-07-18T00:55:07.495Z" },
+ { url = "https://files.pythonhosted.org/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e", size = 41135424, upload-time = "2025-07-18T00:55:11.461Z" },
+ { url = "https://files.pythonhosted.org/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569", size = 42823810, upload-time = "2025-07-18T00:55:16.301Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e", size = 43391538, upload-time = "2025-07-18T00:55:23.82Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c", size = 45120056, upload-time = "2025-07-18T00:55:28.231Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6", size = 26220568, upload-time = "2025-07-18T00:55:32.122Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305, upload-time = "2025-07-18T00:55:35.373Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264, upload-time = "2025-07-18T00:55:39.303Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099, upload-time = "2025-07-18T00:55:42.889Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529, upload-time = "2025-07-18T00:55:47.069Z" },
+ { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883, upload-time = "2025-07-18T00:55:53.069Z" },
+ { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802, upload-time = "2025-07-18T00:55:57.714Z" },
+ { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175, upload-time = "2025-07-18T00:56:01.364Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306, upload-time = "2025-07-18T00:56:04.42Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622, upload-time = "2025-07-18T00:56:07.505Z" },
+ { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094, upload-time = "2025-07-18T00:56:10.994Z" },
+ { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576, upload-time = "2025-07-18T00:56:15.569Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342, upload-time = "2025-07-18T00:56:19.531Z" },
+ { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218, upload-time = "2025-07-18T00:56:23.347Z" },
+ { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551, upload-time = "2025-07-18T00:56:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064, upload-time = "2025-07-18T00:56:30.214Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837, upload-time = "2025-07-18T00:56:33.935Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158, upload-time = "2025-07-18T00:56:37.528Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885, upload-time = "2025-07-18T00:56:41.483Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625, upload-time = "2025-07-18T00:56:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890, upload-time = "2025-07-18T00:56:52.568Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006, upload-time = "2025-07-18T00:56:56.379Z" },
+]
+
+[[package]]
+name = "pycairo"
+version = "1.28.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/d9/412da520de9052b7e80bfc810ec10f5cb3dbfa4aa3e23c2820dc61cdb3d0/pycairo-1.28.0.tar.gz", hash = "sha256:26ec5c6126781eb167089a123919f87baa2740da2cca9098be8b3a6b91cc5fbc", size = 662477, upload-time = "2025-04-14T20:11:08.218Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/67/6e5d6328c6037f0479017f51e97f5cbb79a68ed6e93f671dac17cb47bf2e/pycairo-1.28.0-cp310-cp310-win32.whl", hash = "sha256:53e6dbc98456f789965dad49ef89ce2c62f9a10fc96c8d084e14da0ffb73d8a6", size = 750438, upload-time = "2025-04-14T20:10:43.722Z" },
+ { url = "https://files.pythonhosted.org/packages/00/79/e941186c2275333643504f57711b157ba17e81a2be805346585ad7fd382e/pycairo-1.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:c8ab91a75025f984bc327ada335c787efb61c929ea0512063793cb36cee503d4", size = 841526, upload-time = "2025-04-14T20:10:45.557Z" },
+ { url = "https://files.pythonhosted.org/packages/84/4b/3495baabd3c830bf80bf112a0e645342bfe8f3fc05ed6423444adf62d496/pycairo-1.28.0-cp310-cp310-win_arm64.whl", hash = "sha256:e955328c1a5147bf71ee94e206413ce15e12630296a79788fcd246c80e5337b8", size = 691866, upload-time = "2025-04-16T19:30:17.73Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/94/47d75f4eaac865f32e0550ed42869f8b3c4036f8e2b1e6163e9548f4d44f/pycairo-1.28.0-cp311-cp311-win32.whl", hash = "sha256:0fee15f5d72b13ba5fd065860312493dc1bca6ff2dce200ee9d704e11c94e60a", size = 750441, upload-time = "2025-04-14T20:10:48.311Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/02/1169a2917b043998585f9dd0f36da618947fdf9b6cc0e9ff9852aa4d548b/pycairo-1.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:6339979bfec8b58a06476094a9a5c104bd5a99932ddaff16ca0d9203d2f4482c", size = 841536, upload-time = "2025-04-14T20:10:51.112Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/99/13031086fb4d4ea71568d9ce74e03afbb2e18047f1e6b4e8674851f0414f/pycairo-1.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6ae15392e28ebfc0b35d8dc05d395d3b6be4bad9ad4caecf0fa12c8e7150225", size = 691895, upload-time = "2025-04-16T19:30:20.724Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/d7/206d7945aac5c6c889e7fea4011410d1311455a1d8dfce66106d8d700b7f/pycairo-1.28.0-cp312-cp312-win32.whl", hash = "sha256:c00cfbb7f30eb7ca1d48886712932e2d91e8835a8496f4e423878296ceba573e", size = 750594, upload-time = "2025-04-14T20:10:53.72Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/ba/259fc9a4d3afdad1e5b9db52b9d8a5df67f70dd787167185e8ec8a1af007/pycairo-1.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:d50d190f5033992b55050b9f337ee42a45c3568445d5e5d7987bab96c278d8a6", size = 841767, upload-time = "2025-04-14T20:10:56.711Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/08/aa0903612ea0e8686ad6af58d4fb9cbab8ee0b0831201cddf7abd578d045/pycairo-1.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:957e0340ee1c279d197d4f7cfa96f6d8b48e453eec711fca999748d752468ff4", size = 691687, upload-time = "2025-04-16T19:30:23.729Z" },
+ { url = "https://files.pythonhosted.org/packages/59/a7/c3e5ed55781dfe1b31eb4a2482aeae707671f3d36b0ea53a1722f4a3dfe9/pycairo-1.28.0-cp313-cp313-win32.whl", hash = "sha256:d13352429d8a08a1cb3607767d23d2fb32e4c4f9faa642155383980ec1478c24", size = 750594, upload-time = "2025-04-14T20:10:59.284Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/1c/ebadd290748aff3b6bc35431114d41e7a42f40a4b988c2aaf2dfed5d8156/pycairo-1.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:082aef6b3a9dcc328fa648d38ed6b0a31c863e903ead57dd184b2e5f86790140", size = 841774, upload-time = "2025-04-14T20:11:01.79Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ce/a3f5f1946613cd8a4654322b878c59f273c6e9b01dfadadd3f609070e0b9/pycairo-1.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:026afd53b75291917a7412d9fe46dcfbaa0c028febd46ff1132d44a53ac2c8b6", size = 691675, upload-time = "2025-04-16T19:30:26.565Z" },
+ { url = "https://files.pythonhosted.org/packages/96/1b/1f5da2b2e44b33a5b269ff28af710b0a7903001d9cf8a40d00fc944a5092/pycairo-1.28.0-cp314-cp314-win32.whl", hash = "sha256:d0ab30585f536101ad6f09052fc3895e2a437ba57531ea07223d0e076248025d", size = 766699, upload-time = "2025-07-24T06:36:07.267Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/91/1d5f18bd3ed469b1de8f83bfbf8bd67d23439f075151b66740fd35356190/pycairo-1.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:94f2ed204999ab95a0671a0fa948ffbb9f3d6fb8731fe787917f6d022d9c1c0f", size = 868725, upload-time = "2025-07-24T06:36:09.597Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.23"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.11.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.33.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" },
+ { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" },
+ { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" },
+ { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" },
+ { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" },
+ { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" },
+ { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" },
+ { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" },
+ { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" },
+ { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" },
+ { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" },
+ { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" },
+ { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
+ { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
+ { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
+ { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
+ { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
+ { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" },
+ { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" },
+ { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" },
+ { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" },
+ { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" },
+ { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" },
+ { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" },
+]
+
+[[package]]
+name = "pyee"
+version = "11.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/10/88/c9a028db29f009170db6337a5096476a12448ffce437bbf91458fd5b5767/pyee-11.0.1.tar.gz", hash = "sha256:a642c51e3885a33ead087286e35212783a4e9b8d6514a10a5db4e57ac57b2b29", size = 27877, upload-time = "2023-10-14T17:50:00.259Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/40/bfe7fd2cb55ca7dbb4c56e9f0060567c3e84dd9bdf8a782261f1d2d7c32f/pyee-11.0.1-py3-none-any.whl", hash = "sha256:9bcc9647822234f42c228d88de63d0f9ffa881e87a87f9d36ddf5211f6ac977d", size = 15249, upload-time = "2023-10-14T17:49:57.906Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pygobject"
+version = "3.46.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycairo" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/4a/f24ddf1d20cc4b56affc7921e29928559a06c922eb60077448392792b914/PyGObject-3.46.0.tar.gz", hash = "sha256:481437b05af0a66b7c366ea052710eb3aacbb979d22d30b797f7ec29347ab1e6", size = 723367, upload-time = "2023-09-10T09:56:26.737Z" }
+
+[[package]]
+name = "pyngrok"
+version = "7.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7b/64/e5646daaa4a84ecb368c09d42753f1c6e1a41b38579a36fda8ffe5716d9f/pyngrok-7.3.0.tar.gz", hash = "sha256:f2240e49b9dec9b27eba00f1444cd3cce7ac45d1bcc493a9b76ee63c2da9882c", size = 44266, upload-time = "2025-08-04T16:08:07.015Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ac/6b/664772910199399db3706243488eb3a986bd8435583caef9cf8359eecfaa/pyngrok-7.3.0-py3-none-any.whl", hash = "sha256:708c48276d3a887d4263f0f4bc8171e22d66df6cfa74a4857625c83160603166", size = 25394, upload-time = "2025-08-04T16:08:05.368Z" },
+]
+
+[[package]]
+name = "pynput"
+version = "1.8.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "evdev", marker = "'linux' in sys_platform" },
+ { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
+ { name = "python-xlib", marker = "'linux' in sys_platform" },
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f0/c3/dccf44c68225046df5324db0cc7d563a560635355b3e5f1d249468268a6f/pynput-1.8.1.tar.gz", hash = "sha256:70d7c8373ee98911004a7c938742242840a5628c004573d84ba849d4601df81e", size = 82289, upload-time = "2025-03-17T17:12:01.481Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/4f/ac3fa906ae8a375a536b12794128c5efacade9eaa917a35dfd27ce0c7400/pynput-1.8.1-py2.py3-none-any.whl", hash = "sha256:42dfcf27404459ca16ca889c8fb8ffe42a9fe54f722fd1a3e130728e59e768d2", size = 91693, upload-time = "2025-03-17T17:12:00.094Z" },
+]
+
+[[package]]
+name = "pyobjc-core"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/e9/0b85c81e2b441267bca707b5d89f56c2f02578ef8f3eafddf0e0c0b8848c/pyobjc_core-11.1.tar.gz", hash = "sha256:b63d4d90c5df7e762f34739b39cc55bc63dbcf9fb2fb3f2671e528488c7a87fe", size = 974602, upload-time = "2025-06-14T20:56:34.189Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/c5/9fa74ef6b83924e657c5098d37b36b66d1e16d13bc45c44248c6248e7117/pyobjc_core-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4c7536f3e94de0a3eae6bb382d75f1219280aa867cdf37beef39d9e7d580173c", size = 676323, upload-time = "2025-06-14T20:44:44.675Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/a7/55afc166d89e3fcd87966f48f8bca3305a3a2d7c62100715b9ffa7153a90/pyobjc_core-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ec36680b5c14e2f73d432b03ba7c1457dc6ca70fa59fd7daea1073f2b4157d33", size = 671075, upload-time = "2025-06-14T20:44:46.594Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/09/e83228e878e73bf756749939f906a872da54488f18d75658afa7f1abbab1/pyobjc_core-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:765b97dea6b87ec4612b3212258024d8496ea23517c95a1c5f0735f96b7fd529", size = 677985, upload-time = "2025-06-14T20:44:48.375Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/24/12e4e2dae5f85fd0c0b696404ed3374ea6ca398e7db886d4f1322eb30799/pyobjc_core-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:18986f83998fbd5d3f56d8a8428b2f3e0754fd15cef3ef786ca0d29619024f2c", size = 676431, upload-time = "2025-06-14T20:44:49.908Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/79/031492497624de4c728f1857181b06ce8c56444db4d49418fa459cba217c/pyobjc_core-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8849e78cfe6595c4911fbba29683decfb0bf57a350aed8a43316976ba6f659d2", size = 719330, upload-time = "2025-06-14T20:44:51.621Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/7d/6169f16a0c7ec15b9381f8bf33872baf912de2ef68d96c798ca4c6ee641f/pyobjc_core-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8cb9ed17a8d84a312a6e8b665dd22393d48336ea1d8277e7ad20c19a38edf731", size = 667203, upload-time = "2025-06-14T20:44:53.262Z" },
+ { url = "https://files.pythonhosted.org/packages/49/0f/f5ab2b0e57430a3bec9a62b6153c0e79c05a30d77b564efdb9f9446eeac5/pyobjc_core-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:f2455683e807f8541f0d83fbba0f5d9a46128ab0d5cc83ea208f0bec759b7f96", size = 708807, upload-time = "2025-06-14T20:44:54.851Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-applicationservices"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-framework-coretext" },
+ { name = "pyobjc-framework-quartz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/3f/b33ce0cecc3a42f6c289dcbf9ff698b0d9e85f5796db2e9cb5dadccffbb9/pyobjc_framework_applicationservices-11.1.tar.gz", hash = "sha256:03fcd8c0c600db98fa8b85eb7b3bc31491701720c795e3f762b54e865138bbaf", size = 224842, upload-time = "2025-06-14T20:56:40.648Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/2b/b46566639b13354d348092f932b4debda2e8604c9b1b416eb3619676e997/pyobjc_framework_applicationservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:89aa713f16f1de66efd82f3be77c632ad1068e51e0ef0c2b0237ac7c7f580814", size = 30991, upload-time = "2025-06-14T20:45:17.223Z" },
+ { url = "https://files.pythonhosted.org/packages/39/2d/9fde6de0b2a95fbb3d77ba11b3cc4f289dd208f38cb3a28389add87c0f44/pyobjc_framework_applicationservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cf45d15eddae36dec2330a9992fc852476b61c8f529874b9ec2805c768a75482", size = 30991, upload-time = "2025-06-14T20:45:18.169Z" },
+ { url = "https://files.pythonhosted.org/packages/38/ec/46a5c710e2d7edf55105223c34fed5a7b7cc7aba7d00a3a7b0405d6a2d1a/pyobjc_framework_applicationservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4a85ccd78bab84f7f05ac65ff9be117839dfc09d48c39edd65c617ed73eb01c", size = 31056, upload-time = "2025-06-14T20:45:18.925Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/06/c2a309e6f37bfa73a2a581d3301321b2033e25b249e2a01e417a3c34e799/pyobjc_framework_applicationservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:385a89f4d0838c97a331e247519d9e9745aa3f7427169d18570e3c664076a63c", size = 31072, upload-time = "2025-06-14T20:45:19.707Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/5f/357bf498c27f1b4d48385860d8374b2569adc1522aabe32befd77089c070/pyobjc_framework_applicationservices-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f480fab20f3005e559c9d06c9a3874a1f1c60dde52c6d28a53ab59b45e79d55f", size = 31335, upload-time = "2025-06-14T20:45:20.462Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/b6/797fdd81399fe8251196f29a621ba3f3f04d5c579d95fd304489f5558202/pyobjc_framework_applicationservices-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:e8dee91c6a14fd042f98819dc0ac4a182e0e816282565534032f0e544bfab143", size = 31196, upload-time = "2025-06-14T20:45:21.555Z" },
+ { url = "https://files.pythonhosted.org/packages/68/45/47eba8d7cdf16d778240ed13fb405e8d712464170ed29d0463363a695194/pyobjc_framework_applicationservices-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:a0ce40a57a9b993793b6f72c4fd93f80618ef54a69d76a1da97b8360a2f3ffc5", size = 31446, upload-time = "2025-06-14T20:45:22.313Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-avfoundation"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-framework-coreaudio" },
+ { name = "pyobjc-framework-coremedia" },
+ { name = "pyobjc-framework-quartz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3c/1f/90cdbce1d3b4861cbb17c12adf57daeec32477eb1df8d3f9ab8551bdadfb/pyobjc_framework_avfoundation-11.1.tar.gz", hash = "sha256:6663056cc6ca49af8de6d36a7fff498f51e1a9a7f1bde7afba718a8ceaaa7377", size = 832178, upload-time = "2025-06-14T20:56:46.329Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/b3/be739115eebb03ea38853ad2dcedf8a21da923415b0b6b4dc6f50c737863/pyobjc_framework_avfoundation-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:09542590d1f3aa96d4d1a37712b98fd9657e250d9ea06ecdf2a8a59c837a2cb6", size = 70713, upload-time = "2025-06-14T20:45:51.427Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/48/31286b2b09a619d8047256d7180e0d511be71ab598e5f54f034977b59bbf/pyobjc_framework_avfoundation-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a0ccbdba46b69dec1d12eea52eef56fcd63c492f73e41011bb72508b2aa2d0e", size = 70711, upload-time = "2025-06-14T20:45:52.461Z" },
+ { url = "https://files.pythonhosted.org/packages/43/30/d5d03dd4a508bdaa2156ff379e9e109020de23cbb6316c5865d341aa6db1/pyobjc_framework_avfoundation-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94f065db4e87b1baebb5cf9f464cf9d82c5f903fff192001ebc974d9e3132c7e", size = 70746, upload-time = "2025-06-14T20:45:53.253Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8c/b8ced7700b0e931dc37d14b05e2bead28d2598c887832b3d697da55b1845/pyobjc_framework_avfoundation-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e204d155a09c186601490e4402dcffb2845a5831079e389b47bd6a341fe5ee63", size = 70773, upload-time = "2025-06-14T20:45:54.059Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/4c/086f4713793aaabdb5134debbf1fdc6c7d4ef5a32a6b35529e2e69580ec8/pyobjc_framework_avfoundation-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:dd3965aad0b236b8ac12f216d688c1a22b963f63e7e4fdb7107dd6790e80ee12", size = 71352, upload-time = "2025-06-14T20:45:54.871Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/5f/d5c4b9812e22c6fdf234421f131efae7c3137e838bb9df9be8bb45cde97b/pyobjc_framework_avfoundation-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:1ab2108b652496b13b9758c295f0f6de53b6d12125cf574ddae84ce28044bce1", size = 71208, upload-time = "2025-06-14T20:45:56.057Z" },
+ { url = "https://files.pythonhosted.org/packages/29/d0/dec23e1745a81f5576cba577fa7218d665f36250a8507eaaa83a84579abf/pyobjc_framework_avfoundation-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:5dd6ac6a57f86b7ed5ac0a965ce54328f6ce77816b4a1fbf0d85c06fb251867a", size = 71680, upload-time = "2025-06-14T20:45:57.091Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-cocoa"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/c5/7a866d24bc026f79239b74d05e2cf3088b03263da66d53d1b4cf5207f5ae/pyobjc_framework_cocoa-11.1.tar.gz", hash = "sha256:87df76b9b73e7ca699a828ff112564b59251bb9bbe72e610e670a4dc9940d038", size = 5565335, upload-time = "2025-06-14T20:56:59.683Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/8f/67a7e166b615feb96385d886c6732dfb90afed565b8b1f34673683d73cd9/pyobjc_framework_cocoa-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b27a5bdb3ab6cdeb998443ff3fce194ffae5f518c6a079b832dbafc4426937f9", size = 388187, upload-time = "2025-06-14T20:46:49.74Z" },
+ { url = "https://files.pythonhosted.org/packages/90/43/6841046aa4e257b6276cd23e53cacedfb842ecaf3386bb360fa9cc319aa1/pyobjc_framework_cocoa-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b9a9b8ba07f5bf84866399e3de2aa311ed1c34d5d2788a995bdbe82cc36cfa0", size = 388177, upload-time = "2025-06-14T20:46:51.454Z" },
+ { url = "https://files.pythonhosted.org/packages/68/da/41c0f7edc92ead461cced7e67813e27fa17da3c5da428afdb4086c69d7ba/pyobjc_framework_cocoa-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806de56f06dfba8f301a244cce289d54877c36b4b19818e3b53150eb7c2424d0", size = 388983, upload-time = "2025-06-14T20:46:52.591Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/0b/a01477cde2a040f97e226f3e15e5ffd1268fcb6d1d664885a95ba592eca9/pyobjc_framework_cocoa-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:54e93e1d9b0fc41c032582a6f0834befe1d418d73893968f3f450281b11603da", size = 389049, upload-time = "2025-06-14T20:46:53.757Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/64cf2661f6ab7c124d0486ec6d1d01a9bb2838a0d2a46006457d8c5e6845/pyobjc_framework_cocoa-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fd5245ee1997d93e78b72703be1289d75d88ff6490af94462b564892e9266350", size = 393110, upload-time = "2025-06-14T20:46:54.894Z" },
+ { url = "https://files.pythonhosted.org/packages/33/87/01e35c5a3c5bbdc93d5925366421e10835fcd7b23347b6c267df1b16d0b3/pyobjc_framework_cocoa-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:aede53a1afc5433e1e7d66568cc52acceeb171b0a6005407a42e8e82580b4fc0", size = 392644, upload-time = "2025-06-14T20:46:56.503Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/7c/54afe9ffee547c41e1161691e72067a37ed27466ac71c089bfdcd07ca70d/pyobjc_framework_cocoa-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:1b5de4e1757bb65689d6dc1f8d8717de9ec8587eb0c4831c134f13aba29f9b71", size = 396742, upload-time = "2025-06-14T20:46:57.64Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coreaudio"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/39/c0/4ab6005cf97e534725b0c14b110d4864b367c282b1c5b0d8f42aad74a83f/pyobjc_framework_coreaudio-11.1.tar.gz", hash = "sha256:b7b89540ae7efc6c1e3208ac838ef2acfc4d2c506dd629d91f6b3b3120e55c1b", size = 141032, upload-time = "2025-06-14T20:57:04.348Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b8/fd/59bcaa6436c27d3da4ea147da4e6f723606317e38e7101f8b191b687176d/pyobjc_framework_coreaudio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:551c8aac6fdfbd34c3e2d4ce90b36a411e81be20581b978fa4da1a495489792d", size = 35380, upload-time = "2025-06-14T20:47:13.306Z" },
+ { url = "https://files.pythonhosted.org/packages/54/1d/81339c1087519a9f125396c717b85a05b49c2c54137bdf4ca01c1ccb6239/pyobjc_framework_coreaudio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:73a46f0db2fa8ca2e8c47c3ddcc2751e67a0f8600246a6718553b15ee0dbbdb6", size = 35383, upload-time = "2025-06-14T20:47:14.234Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/fe/c43521642db98a4ec29fa535781c1316342bb52d5fc709696cbb1e8ca6cd/pyobjc_framework_coreaudio-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2538d1242dab4e27efb346eafbad50594e7e95597fa7220f0bab2099c825da55", size = 36765, upload-time = "2025-06-14T20:47:15.344Z" },
+ { url = "https://files.pythonhosted.org/packages/82/9b/24d03ace273585de2d04385f06b895ce92caf8f5af430b060618ebce9dbe/pyobjc_framework_coreaudio-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f73d996df1e721931d9f78050e1708735a173dbe3a76d9c71fb36e04f7208478", size = 36779, upload-time = "2025-06-14T20:47:16.123Z" },
+ { url = "https://files.pythonhosted.org/packages/91/23/aa78365e45d0d04fc37e21cf7d69dc0d11e17b564e83cb5bcd98e89cdf45/pyobjc_framework_coreaudio-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:67dae111b78d91c26c753dbfbccc3ea5498cfda3dfe83c6f3778628b435e1e7b", size = 38480, upload-time = "2025-06-14T20:47:16.911Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/58/fc6d752a68f28567fa6d6d6a229122c829e2251f79ec7304fe0572e0fdcd/pyobjc_framework_coreaudio-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:9527a16a2b88b37bace578d499f21229f9a33b9afdcdd35d4f44374cb8eb9ab6", size = 36910, upload-time = "2025-06-14T20:47:17.69Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/4c/c1c5624418dea005d9965ba690d3649afc33371ade213841ab51922af751/pyobjc_framework_coreaudio-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:6ba8b67f185c0e3f26b17ae525cee3f411bc8d6e9c9a8bfd899a28f594623d2f", size = 38567, upload-time = "2025-06-14T20:47:18.45Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coremedia"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/95/5d/81513acd219df77a89176f1574d936b81ad6f6002225cabb64d55efb7e8d/pyobjc_framework_coremedia-11.1.tar.gz", hash = "sha256:82cdc087f61e21b761e677ea618a575d4c0dbe00e98230bf9cea540cff931db3", size = 216389, upload-time = "2025-06-14T20:57:09.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/09/5e/396621c2b29353a3cb6d2caa4116583bf1073aedaf2ef196fec85d983696/pyobjc_framework_coremedia-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91231957d25b6d191983166cf218189b5a01e267dadde35eb3a4c359dc473ccb", size = 29113, upload-time = "2025-06-14T20:47:47.978Z" },
+ { url = "https://files.pythonhosted.org/packages/32/48/811ccea77d2c0d8156a489e2900298502eb6648d9c041c7f0c514c8f8a29/pyobjc_framework_coremedia-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aacf47006e1c6bf6124fb2b5016a8d5fd5cf504b6b488f9eba4e389ab0f0a051", size = 29118, upload-time = "2025-06-14T20:47:48.895Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d1/b3d004d6a2d2188d196779d92fe8cfa2533f5722cd216fbc4f0cffc63b24/pyobjc_framework_coremedia-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea5055298af91e463ffa7977d573530f9bada57b8f2968dcc76a75e339b9a598", size = 29015, upload-time = "2025-06-14T20:47:49.655Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/23/cafd29011d14eac27fc55770157ebb8e02ffed9f75e01f24e97616417c4c/pyobjc_framework_coremedia-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7ecdb64c743ffe9fd3949c7cc9109891b9f399a0852717fcb969d33c4e7ba527", size = 29031, upload-time = "2025-06-14T20:47:50.395Z" },
+ { url = "https://files.pythonhosted.org/packages/de/a6/ca85b7d9d000e8e2748bcacde356278cb90f6ca9aed54dce6a42d1716ba8/pyobjc_framework_coremedia-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:969ce357c616f6835f47e27d1e73964374cdb671476571dfd358894a8ced06f2", size = 29094, upload-time = "2025-06-14T20:47:51.318Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/3d/56d530cf504a6eef84f51c8f6f845af8b947f6108e41db5e0b5189d5a667/pyobjc_framework_coremedia-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:bf1da05c297776c297ab3489ebf18d954efdff530acbdd6e70c32be811e20ec6", size = 29043, upload-time = "2025-06-14T20:47:52.092Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/bc/b237ecd4954a0f07450469236ca45412edb7d8715ff7fc175ac519e7c472/pyobjc_framework_coremedia-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:aa942d9ad0cf5bc4d3ede8779c3fac2f04cf3857687f2fb8505bae3378d04b95", size = 29111, upload-time = "2025-06-14T20:47:53.083Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coretext"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-framework-quartz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/65/e9/d3231c4f87d07b8525401fd6ad3c56607c9e512c5490f0a7a6abb13acab6/pyobjc_framework_coretext-11.1.tar.gz", hash = "sha256:a29bbd5d85c77f46a8ee81d381b847244c88a3a5a96ac22f509027ceceaffaf6", size = 274702, upload-time = "2025-06-14T20:57:16.059Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/0c/0117d5353b1d18f8f8dd1e0f48374e4819cfcf3e8c34c676353e87320e8f/pyobjc_framework_coretext-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:515be6beb48c084ee413c00c4e9fbd6e730c1b8a24270f4c618fc6c7ba0011ce", size = 30072, upload-time = "2025-06-14T20:48:33.341Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/59/d6cc5470157cfd328b2d1ee2c1b6f846a5205307fce17291b57236d9f46e/pyobjc_framework_coretext-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4f4d2d2a6331fa64465247358d7aafce98e4fb654b99301a490627a073d021e", size = 30072, upload-time = "2025-06-14T20:48:34.248Z" },
+ { url = "https://files.pythonhosted.org/packages/32/67/9cc5189c366e67dc3e5b5976fac73cc6405841095f795d3fa0d5fc43d76a/pyobjc_framework_coretext-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1597bf7234270ee1b9963bf112e9061050d5fb8e1384b3f50c11bde2fe2b1570", size = 30175, upload-time = "2025-06-14T20:48:35.023Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/d1/6ec2ef4f8133177203a742d5db4db90bbb3ae100aec8d17f667208da84c9/pyobjc_framework_coretext-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:37e051e8f12a0f47a81b8efc8c902156eb5bc3d8123c43e5bd4cebd24c222228", size = 30180, upload-time = "2025-06-14T20:48:35.766Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/84/d4a95e49f6af59503ba257fbed0471b6932f0afe8b3725c018dd3ba40150/pyobjc_framework_coretext-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:56a3a02202e0d50be3c43e781c00f9f1859ab9b73a8342ff56260b908e911e37", size = 30768, upload-time = "2025-06-14T20:48:36.869Z" },
+ { url = "https://files.pythonhosted.org/packages/64/4c/16e1504e06a5cb23eec6276835ddddb087637beba66cf84b5c587eba99be/pyobjc_framework_coretext-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:15650ba99692d00953e91e53118c11636056a22c90d472020f7ba31500577bf5", size = 30155, upload-time = "2025-06-14T20:48:37.948Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/a4/cbfa9c874b2770fb1ba5c38c42b0e12a8b5aa177a5a86d0ad49b935aa626/pyobjc_framework_coretext-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:fb27f66a56660c31bb956191d64b85b95bac99cfb833f6e99622ca0ac4b3ba12", size = 30768, upload-time = "2025-06-14T20:48:38.734Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-quartz"
+version = "11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core" },
+ { name = "pyobjc-framework-cocoa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c7/ac/6308fec6c9ffeda9942fef72724f4094c6df4933560f512e63eac37ebd30/pyobjc_framework_quartz-11.1.tar.gz", hash = "sha256:a57f35ccfc22ad48c87c5932818e583777ff7276605fef6afad0ac0741169f75", size = 3953275, upload-time = "2025-06-14T20:58:17.924Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/62/f8d9bb4cba92d5f220327cf1def2c2c5be324880d54ee57e7bea43aa28b2/pyobjc_framework_quartz-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b5ef75c416b0209e25b2eb07a27bd7eedf14a8c6b2f968711969d45ceceb0f84", size = 215586, upload-time = "2025-06-14T20:53:34.018Z" },
+ { url = "https://files.pythonhosted.org/packages/77/cb/38172fdb350b3f47e18d87c5760e50f4efbb4da6308182b5e1310ff0cde4/pyobjc_framework_quartz-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d501fe95ef15d8acf587cb7dc4ab4be3c5a84e2252017da8dbb7df1bbe7a72a", size = 215565, upload-time = "2025-06-14T20:53:35.262Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/37/ee6e0bdd31b3b277fec00e5ee84d30eb1b5b8b0e025095e24ddc561697d0/pyobjc_framework_quartz-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9ac806067541917d6119b98d90390a6944e7d9bd737f5c0a79884202327c9204", size = 216410, upload-time = "2025-06-14T20:53:36.346Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/27/4f4fc0e6a0652318c2844608dd7c41e49ba6006ee5fb60c7ae417c338357/pyobjc_framework_quartz-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43a1138280571bbf44df27a7eef519184b5c4183a588598ebaaeb887b9e73e76", size = 216816, upload-time = "2025-06-14T20:53:37.358Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/8a/1d15e42496bef31246f7401aad1ebf0f9e11566ce0de41c18431715aafbc/pyobjc_framework_quartz-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b23d81c30c564adf6336e00b357f355b35aad10075dd7e837cfd52a9912863e5", size = 221941, upload-time = "2025-06-14T20:53:38.34Z" },
+ { url = "https://files.pythonhosted.org/packages/32/a8/a3f84d06e567efc12c104799c7fd015f9bea272a75f799eda8b79e8163c6/pyobjc_framework_quartz-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:07cbda78b4a8fcf3a2d96e047a2ff01f44e3e1820f46f0f4b3b6d77ff6ece07c", size = 221312, upload-time = "2025-06-14T20:53:39.435Z" },
+ { url = "https://files.pythonhosted.org/packages/76/ef/8c08d4f255bb3efe8806609d1f0b1ddd29684ab0f9ffb5e26d3ad7957b29/pyobjc_framework_quartz-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:39d02a3df4b5e3eee1e0da0fb150259476910d2a9aa638ab94153c24317a9561", size = 226353, upload-time = "2025-06-14T20:53:40.655Z" },
+]
+
+[[package]]
+name = "pyopengl"
+version = "3.1.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/16/912b7225d56284859cd9a672827f18be43f8012f8b7b932bc4bd959a298e/pyopengl-3.1.10.tar.gz", hash = "sha256:c4a02d6866b54eb119c8e9b3fb04fa835a95ab802dd96607ab4cdb0012df8335", size = 1915580, upload-time = "2025-08-18T02:33:01.76Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl", hash = "sha256:794a943daced39300879e4e47bd94525280685f42dbb5a998d336cfff151d74f", size = 3194996, upload-time = "2025-08-18T02:32:59.902Z" },
+]
+
+[[package]]
+name = "pyreadline3"
+version = "3.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" },
+]
+
+[[package]]
+name = "pyserial"
+version = "3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "8.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
+]
+
+[[package]]
+name = "pytest-asyncio"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
+ { name = "pytest" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" },
+]
+
+[[package]]
+name = "python-utils"
+version = "3.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/13/4c/ef8b7b1046d65c1f18ca31e5235c7d6627ca2b3f389ab1d44a74d22f5cc9/python_utils-3.9.1.tar.gz", hash = "sha256:eb574b4292415eb230f094cbf50ab5ef36e3579b8f09e9f2ba74af70891449a0", size = 35403, upload-time = "2024-11-26T00:38:58.736Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/69/31c82567719b34d8f6b41077732589104883771d182a9f4ff3e71430999a/python_utils-3.9.1-py2.py3-none-any.whl", hash = "sha256:0273d7363c7ad4b70999b2791d5ba6b55333d6f7a4e4c8b6b39fb82b5fab4613", size = 32078, upload-time = "2024-11-26T00:38:57.488Z" },
+]
+
+[[package]]
+name = "python-xlib"
+version = "0.33"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload-time = "2022-12-25T18:53:00.824Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185, upload-time = "2022-12-25T18:52:58.662Z" },
+]
+
+[[package]]
+name = "pyusb"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/6b/ce3727395e52b7b76dfcf0c665e37d223b680b9becc60710d4bc08b7b7cb/pyusb-1.3.1.tar.gz", hash = "sha256:3af070b607467c1c164f49d5b0caabe8ac78dbed9298d703a8dbf9df4052d17e", size = 77281, upload-time = "2025-01-08T23:45:01.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/b8/27e6312e86408a44fe16bd28ee12dd98608b39f7e7e57884a24e8f29b573/pyusb-1.3.1-py3-none-any.whl", hash = "sha256:bf9b754557af4717fe80c2b07cc2b923a9151f5c08d17bdb5345dac09d6a0430", size = 58465, upload-time = "2025-01-08T23:45:00.029Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" },
+ { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" },
+ { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
+ { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" },
+ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" },
+ { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
+]
+
+[[package]]
+name = "pyzmq"
+version = "27.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "implementation_name == 'pypy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" },
+ { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" },
+ { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" },
+ { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" },
+ { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" },
+ { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" },
+ { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" },
+ { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" },
+ { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" },
+ { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" },
+ { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" },
+ { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" },
+ { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" },
+ { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" },
+ { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" },
+ { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" },
+ { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" },
+ { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" },
+ { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" },
+ { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" },
+ { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" },
+ { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" },
+ { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" },
+]
+
+[[package]]
+name = "reachy-mini"
+version = "1.1.1"
+source = { editable = "." }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "asgiref" },
+ { name = "cv2-enumerate-cameras" },
+ { name = "eclipse-zenoh" },
+ { name = "fastapi" },
+ { name = "huggingface-hub" },
+ { name = "jinja2" },
+ { name = "libusb-package" },
+ { name = "log-throttling" },
+ { name = "numpy" },
+ { name = "opencv-python" },
+ { name = "pip" },
+ { name = "psutil" },
+ { name = "pyserial" },
+ { name = "pyusb" },
+ { name = "reachy-mini-motor-controller" },
+ { name = "reachy-mini-rust-kinematics" },
+ { name = "scipy" },
+ { name = "sounddevice" },
+ { name = "soundfile" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[package.optional-dependencies]
+dev = [
+ { name = "mujoco" },
+ { name = "mypy" },
+ { name = "onshape-to-robot" },
+ { name = "placo" },
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "rerun-sdk" },
+ { name = "ruff" },
+ { name = "rustypot" },
+ { name = "types-requests" },
+ { name = "urdf-parser-py" },
+]
+examples = [
+ { name = "pynput" },
+]
+gstreamer = [
+ { name = "gst-signalling" },
+ { name = "pygobject" },
+]
+mujoco = [
+ { name = "mujoco" },
+]
+nn-kinematics = [
+ { name = "onnxruntime" },
+]
+placo-kinematics = [
+ { name = "placo" },
+]
+rerun = [
+ { name = "rerun-sdk" },
+ { name = "urdf-parser-py" },
+]
+wireless-version = [
+ { name = "gpiozero" },
+ { name = "lgpio" },
+ { name = "nmcli" },
+ { name = "pollen-bmi088-imu-library" },
+ { name = "semver" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "aiohttp" },
+ { name = "asgiref" },
+ { name = "cv2-enumerate-cameras", specifier = ">=1.2.1" },
+ { name = "eclipse-zenoh", specifier = ">=1.4.0" },
+ { name = "fastapi" },
+ { name = "gpiozero", marker = "extra == 'wireless-version'", specifier = ">=2.0.0" },
+ { name = "gst-signalling", marker = "extra == 'gstreamer'", specifier = ">=1.1.2" },
+ { name = "huggingface-hub", specifier = "==0.34.4" },
+ { name = "jinja2" },
+ { name = "lgpio", marker = "extra == 'wireless-version'", specifier = ">=0.2.2.0" },
+ { name = "libusb-package", specifier = ">=1.0.26.3" },
+ { name = "log-throttling", specifier = "==0.0.3" },
+ { name = "mujoco", marker = "extra == 'dev'", specifier = "==3.3.0" },
+ { name = "mujoco", marker = "extra == 'mujoco'", specifier = "==3.3.0" },
+ { name = "mypy", marker = "extra == 'dev'", specifier = "==1.18.2" },
+ { name = "nmcli", marker = "extra == 'wireless-version'", specifier = ">=1.5" },
+ { name = "numpy", specifier = ">=2.2.5" },
+ { name = "onnxruntime", marker = "extra == 'nn-kinematics'", specifier = "==1.22.1" },
+ { name = "onshape-to-robot", marker = "extra == 'dev'", specifier = "==1.7.6" },
+ { name = "opencv-python", specifier = "<=5.0" },
+ { name = "pip", specifier = ">=25" },
+ { name = "placo", marker = "extra == 'dev'", specifier = "==0.9.14" },
+ { name = "placo", marker = "extra == 'placo-kinematics'", specifier = "==0.9.14" },
+ { name = "pollen-bmi088-imu-library", marker = "extra == 'wireless-version'" },
+ { name = "pre-commit", marker = "extra == 'dev'" },
+ { name = "psutil" },
+ { name = "pygobject", marker = "extra == 'gstreamer'", specifier = ">=3.42.2,<=3.46.0" },
+ { name = "pynput", marker = "extra == 'examples'" },
+ { name = "pyserial" },
+ { name = "pytest", marker = "extra == 'dev'" },
+ { name = "pytest-asyncio", marker = "extra == 'dev'" },
+ { name = "pyusb", specifier = ">=1.2.1" },
+ { name = "reachy-mini-motor-controller", specifier = ">=1.4.2" },
+ { name = "reachy-mini-rust-kinematics", specifier = ">=1.0.2" },
+ { name = "rerun-sdk", marker = "extra == 'dev'", specifier = ">=0.27.2" },
+ { name = "rerun-sdk", marker = "extra == 'rerun'", specifier = ">=0.27.2" },
+ { name = "ruff", marker = "extra == 'dev'", specifier = "==0.12.0" },
+ { name = "rustypot", marker = "extra == 'dev'", specifier = ">=1.4.0" },
+ { name = "scipy", specifier = ">=1.15.3,<2.0.0" },
+ { name = "semver", marker = "extra == 'wireless-version'", specifier = ">=3,<4" },
+ { name = "sounddevice", specifier = "==0.5.1" },
+ { name = "soundfile", specifier = "==0.13.1" },
+ { name = "types-requests", marker = "extra == 'dev'" },
+ { name = "urdf-parser-py", marker = "extra == 'dev'", specifier = "==0.0.4" },
+ { name = "urdf-parser-py", marker = "extra == 'rerun'", specifier = "==0.0.4" },
+ { name = "uvicorn", extras = ["standard"] },
+]
+provides-extras = ["dev", "examples", "mujoco", "nn-kinematics", "placo-kinematics", "gstreamer", "rerun", "wireless-version"]
+
+[[package]]
+name = "reachy-mini-motor-controller"
+version = "1.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4a/8d/353b13636ebffae848c0d0b8c4bfc15c4673b059f62f9c310ee98986b0b7/reachy_mini_motor_controller-1.4.2.tar.gz", hash = "sha256:bd6e9f5c13394bc57478a09e76e01c28854ae0c80a9335a3a1b78dc80e0ee150", size = 28248, upload-time = "2025-11-25T17:56:55.263Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a3/54/1dfb498cea7c568a83878b77348abe4d93a1233dab22be566c45f89e154c/reachy_mini_motor_controller-1.4.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:193a11b9c5a3e85bb21305c09c67990c45c832bbb1d8ab836feb78700dadcd18", size = 586874, upload-time = "2025-11-25T17:56:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/86/ca36d720dcc497c7557e467d6e891e26cd86ce0d44236750d689a6fa306d/reachy_mini_motor_controller-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8226ed36f5391112a1b0f1ca2658eee24e0e12233b33637b9ece83fa7e0f3e94", size = 572458, upload-time = "2025-11-25T17:56:16.317Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/46/41a03359e0c70cc34f73b569d3090d435c15ea248b01bf978a9eee259eaa/reachy_mini_motor_controller-1.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83607921aa369c8e0b3a89bd845c7f6989fc29e1847d46f01eb3af2f27f04b99", size = 641152, upload-time = "2025-11-25T17:55:55.287Z" },
+ { url = "https://files.pythonhosted.org/packages/80/cb/9e395e02f47c301bc32ab085f48a9be9c741911b9d2fc0d1131d46a72660/reachy_mini_motor_controller-1.4.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a31e530b41514a81ad43db5ff31d4a77a6f91034d36d7646853beeaafa7a150d", size = 647600, upload-time = "2025-11-25T17:56:01.073Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/45/81b45006257c6de17db7b8420d9039f23367db56ba9a29a78962dc6bcf42/reachy_mini_motor_controller-1.4.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5216ee318c9f20d5bc15e8aa8c96d60b989cf4e4ebf762edf20f6de2ee32ecb", size = 695967, upload-time = "2025-11-25T17:56:06.522Z" },
+ { url = "https://files.pythonhosted.org/packages/40/88/4db598e736ab2cf8528ac875982d60477a904949549d80ee25200e3559aa/reachy_mini_motor_controller-1.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e081f5960143880db1e900108a1065ebe789772de5fa2838a1fa9903dbec7d5", size = 662013, upload-time = "2025-11-25T17:56:11.125Z" },
+ { url = "https://files.pythonhosted.org/packages/99/00/839e6a9aa5005b239a0b06543f413421471550a0c660c54ab841805fa6ec/reachy_mini_motor_controller-1.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:30acdae78c8ef86bbd28b86d644a13c3c3f5c1fafafc14af8de03e509f188c83", size = 824336, upload-time = "2025-11-25T17:56:29.28Z" },
+ { url = "https://files.pythonhosted.org/packages/75/64/cb81d550f1c36c2d14405622bb9fd666f2c6c100b504ac48c2af34ef0ad4/reachy_mini_motor_controller-1.4.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ebb98062d9894790a6028370c35e44e996b67bc5668bd0e4ba2973800056ca64", size = 911952, upload-time = "2025-11-25T17:56:35.665Z" },
+ { url = "https://files.pythonhosted.org/packages/af/b8/9187fe5a246d2643a65282e0ecf511e8abe88634054660b3611c883fee50/reachy_mini_motor_controller-1.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a4283b91055fa66de52cedcce198f3fb262e4a1404346713c19f93d92a06f21", size = 854328, upload-time = "2025-11-25T17:56:41.999Z" },
+ { url = "https://files.pythonhosted.org/packages/35/92/3cd324b1f9934e1ca82af73addb54c0ed2e212e897b4b1e054c887672ce3/reachy_mini_motor_controller-1.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aa3c60cdbe6f7977a5fb6aaa11ced374b931c730f1ae8f68e87345012f810f53", size = 816960, upload-time = "2025-11-25T17:56:48.487Z" },
+ { url = "https://files.pythonhosted.org/packages/54/09/29e7bbfbdbb1de9001341219df98314b10887baf53d1580f3c3db51c6eca/reachy_mini_motor_controller-1.4.2-cp310-cp310-win32.whl", hash = "sha256:a391ca67ab2bcd981ea7139b6171f3ba7f5b3b495bd85655c8d804278cc2ea8e", size = 378120, upload-time = "2025-11-25T17:57:01.73Z" },
+ { url = "https://files.pythonhosted.org/packages/56/48/0b4a252484518e261377304c77327daa710b93b8a23c1b378a5c92337268/reachy_mini_motor_controller-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:38a8c63167b1121a208a3fcae65c17b5222ae95acf80245791c3a51240506fff", size = 401212, upload-time = "2025-11-25T17:56:56.039Z" },
+ { url = "https://files.pythonhosted.org/packages/87/20/ac97ac49969c02e0384888c73cccf0b50a7bba48b18842a96b082a8c8e89/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f405d5f948abd26e423f156fc0d165b69ec964d3a4cbfa7d2d952313cc0ff972", size = 586684, upload-time = "2025-11-25T17:56:24.378Z" },
+ { url = "https://files.pythonhosted.org/packages/68/21/796b9fd0616600b93d5b74ce2b4eb0f525f2773aae8635a8c0506a4145d7/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:835e1d7e9383615b2684fb97cf18065f80498bb7d7d9ba980f8fe295cda68109", size = 572449, upload-time = "2025-11-25T17:56:17.627Z" },
+ { url = "https://files.pythonhosted.org/packages/07/bd/661f6a44bd436c069d38ba0fdc1c1524178c4d039dcd4326fb1918bccf91/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8403bb4b6c23c088493967c8445e5cd1ec4784f5c2ae1dab289fb9ba456055b", size = 642034, upload-time = "2025-11-25T17:55:56.725Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/82/12b52b5eeee59387866db9370e43894f66dda8feedc15be38a6963be3e9a/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1cf6d2a6dc10b5f8acc4c61a89e2e201eca84c6be741becbf9775084d29fc6e", size = 647692, upload-time = "2025-11-25T17:56:02.499Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/a0/4ad6ca320e303de61135219c3b9953b1ec1f7375181dbaaa98a43b65ecae/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a366aa59f9bde4f80f6c03757e431a3cea80f0b18992401169b484cf8928d14", size = 696265, upload-time = "2025-11-25T17:56:07.722Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/98/14cb24c497c295f91b11144bf639304116630de6862ba3095fe3a18dc3ff/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df475384deadcea1c8b430839addecb84126002d54649283cce5e6465d51b543", size = 660553, upload-time = "2025-11-25T17:56:12.631Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/cb/9bc5c91075d5e551deb2d93fae89062bb94b5edd5f658b66c97c11c460d8/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e09e99bb325405f414013ce7d13f7ed36af5b9278dce2ee6bda9ea2affe6baf", size = 825331, upload-time = "2025-11-25T17:56:30.458Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/fd404636cb980604ed82dea5e7ec9c4de78a76324d36ff9efc55bb7549a7/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2f35c0b54ed396b8d9eb98dfe6dcc6d3b059433c2181030591dc84483531a25b", size = 911925, upload-time = "2025-11-25T17:56:36.802Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/1c/6d48c4c3c5c9705e20265db04f040f42d1955cb0ded817e8915684999386/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:af23d02daff28470be0e775ce63666bea06ad24ce948649751f7122996d5363d", size = 854410, upload-time = "2025-11-25T17:56:43.296Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b8/55955b7b47c8ed65bda69a221cd4805bad3d07c3c95b7002e67deec43dff/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:610d74c3cfc28854137d00d206a6b4c4942a371376611781a244376fe3175277", size = 815823, upload-time = "2025-11-25T17:56:50.274Z" },
+ { url = "https://files.pythonhosted.org/packages/12/7c/20672ab40d1d25cdddf49cb969bfa36043d9b760e4e82275a32853846192/reachy_mini_motor_controller-1.4.2-cp311-cp311-win32.whl", hash = "sha256:abe319839f78cc3e4e8ed0ced0bc519f97bfc1e6ff6b320dc8f84b772123237d", size = 377969, upload-time = "2025-11-25T17:57:02.781Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/5e/0725df6e698fe9a83eff0f86d61c5e62878c864ce521c1a1c12645409bc5/reachy_mini_motor_controller-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:d8f7ea29ca78ee86206989c30066e17b213f4cc8975f0a275f092588cd2debc8", size = 401027, upload-time = "2025-11-25T17:56:57.17Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/6a/af2786310d5ccf84a48ff0153c7be2346ef3730ca540b20d998020c36cb8/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a41cc1bdc08b2fbba681804c6143319a6b4ef2d30093910f9776db01c5ba9595", size = 583808, upload-time = "2025-11-25T17:56:25.635Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/d3/6354fd9b34b89d2b5a639c56ae05f393d2ae7cef187bfcce60a71a40202d/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af3cdd5197f57108044af81166dd755214f469c368e79221521b9a698da45223", size = 570332, upload-time = "2025-11-25T17:56:18.828Z" },
+ { url = "https://files.pythonhosted.org/packages/da/eb/d30bc7517a2f7c860b95978af73beede33819e717978d5560f4b5b8a2953/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed2537a512098664d73a7575af763acd5446fc2325f37cf3b9b84d37368b5f7f", size = 641541, upload-time = "2025-11-25T17:55:58.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/63/8f9c091d0b210ebc6aa7762fa09155456a088df987d73dc39cda4fb9c241/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dd7392b9fef86cca8c12d5861d8f17e2eb7e1d69b49c65fbf54e1410f9567", size = 648054, upload-time = "2025-11-25T17:56:03.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/15/a1bebc7acfcb02c12ba32e00dc1f9346fdf2468e441f983daf86ee4c7df3/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3f6ddd91e0228b3396f2c027f3e55eac0d3c4f280968ec73f2bdac37cdeda0", size = 696773, upload-time = "2025-11-25T17:56:08.895Z" },
+ { url = "https://files.pythonhosted.org/packages/06/9f/a3ab6942383198e4c105075582c2355350ef33f90ef4f877105ada181e88/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0b357e49773bcd338e24ed17c4a93fb1890c97513c4cd07eec8c4f3ba06bc48", size = 660676, upload-time = "2025-11-25T17:56:13.77Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/09/7da09c39d2722d6323ed719de95f2f7777699b0049b81d25aaff9b0cc2db/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:dd05b4bbf2aa30674b2955edf8fefbdb90b522077b3c549719afd4a1d2dd325b", size = 824841, upload-time = "2025-11-25T17:56:31.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/68/2c4e8160a3d8e0e20a61ad491dccb9d17febbaa434da83e38060a7517fc9/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:377d47674a0429a01f509d9fe9b52365a715ca132bfdf4956a3b5bb219723941", size = 912195, upload-time = "2025-11-25T17:56:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/14/5f/a547e59efd62518afbb616c9e3bc2f057662fdd209f75b218ef1917bcd7b/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:742c1e077669c198a4261acf3213789ed679846a25714e74e9774984862bbc7a", size = 855751, upload-time = "2025-11-25T17:56:44.595Z" },
+ { url = "https://files.pythonhosted.org/packages/76/58/8720be3e52e302052c6d10e3c0937271e8c376d6e5a10b2dac480c94c7f2/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9b2eb49965f2dba93a1d251b0beb4b22b81f5b18946d53ff8761097011ffaa43", size = 815778, upload-time = "2025-11-25T17:56:51.74Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/52/7af3228652ce3503243cbce68d736af00b11afd07f0f96effa6faaca677d/reachy_mini_motor_controller-1.4.2-cp312-cp312-win32.whl", hash = "sha256:e59bd6a1b89b94da835b1fbe3f483470ea0471fdb1b92d0d4bbb93a24ab7a1aa", size = 377018, upload-time = "2025-11-25T17:57:03.815Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/7a/090705f6e62ec10107ea20e78fd8b8f26f6421b9db49f7bf5e275084aa02/reachy_mini_motor_controller-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:ae99c65942130ce04a01d50dae359694a42fa91ede864ead0defd9a693b30b05", size = 399902, upload-time = "2025-11-25T17:56:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ac/fc016600786fbcdc8d048f7e42c946aa1a488e382e08b83e39934ee5f381/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:43064e6c97be9f6fb2494ebf56dda18daa0366cd70b32ee548818c908d8b3564", size = 583599, upload-time = "2025-11-25T17:56:27.042Z" },
+ { url = "https://files.pythonhosted.org/packages/07/c7/c79c33cf46bbe6301fef5520445649ce10a85a30754fce975df41f7b80b7/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ebe3f763c011ecd3dc4bd017dfc886ecaf455d9866cab1ce8b81116d22eb6a3d", size = 570309, upload-time = "2025-11-25T17:56:20.391Z" },
+ { url = "https://files.pythonhosted.org/packages/78/e0/d301987769c1504549c06cf0ce8e132df48bde0f47d28a8768c25b7ed8ae/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c86146d3631dd73efb77d591f8a33c288b9ad394d3a6805c0685f3c5ec05266f", size = 641472, upload-time = "2025-11-25T17:55:59.645Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/52/7884ee1c04b480f8e34b4c97697a0977d314a22f6766fd5e246dfbddb404/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36052e08d331ae768f4b562e950eaa767ae4ee8a91e9334498c1bcbafe841514", size = 648510, upload-time = "2025-11-25T17:56:05.286Z" },
+ { url = "https://files.pythonhosted.org/packages/03/56/619bb25bb31f96d6172d1bfd5bbf516b5985b595f6e1c7c82481fff95c55/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b2ac6712f7a5493bb3be276fa53bb9b9e4770f13783f26ff0a18e7fc6f800d4", size = 697241, upload-time = "2025-11-25T17:56:09.99Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/b2/c82a31cf252ff6fb4d4c59a208d8375159cc796326812b7e946eea5c00a4/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fcb404dc2021175d4339fb3a8f9334cd999bf8a0369e0dacb02c430edf46a0b", size = 660207, upload-time = "2025-11-25T17:56:15.23Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6e/b2d9aecc4144cffcee14b023da20b8850a11cd35375561a7e60be7132e10/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f482f61eecdeb72a7722399d0d549872a239023c263968f9e2ffac9ee69687dc", size = 824941, upload-time = "2025-11-25T17:56:33.129Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/68/72b7d8a405fe7b1785b2caaaf917958dc6691015f6c15089bfee8b35f65d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:88e1a5c1d4afe4e695d589715f0628536fe7399ec6d2751025dd785f4282a62e", size = 912805, upload-time = "2025-11-25T17:56:39.585Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/62/4ce77e08ea5dc428835202723671026f52d46e20ee2642af24ecd2c64858/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2d5c9e2645d7c6b29a8a46b4ecef72540bb2c0f983349cfcaf7c681386e7a002", size = 856256, upload-time = "2025-11-25T17:56:45.894Z" },
+ { url = "https://files.pythonhosted.org/packages/71/a6/c031586c9dd8fc735b427c5fcdd20d9360c02a6c61ab3cbb59589ad15d9d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffb5331150c0cdfac9910d341e82d788bc173e473e893e3f8e54938ede1e3523", size = 815565, upload-time = "2025-11-25T17:56:52.837Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/e3/5316b60491ecf3384b1b51db49317bc175561411edcc83fe1398b68736f4/reachy_mini_motor_controller-1.4.2-cp313-cp313-win32.whl", hash = "sha256:6edcf88f5d4c1bf3948328a6f65cd4518fda8cefbe55fb884fda088f363c2b60", size = 377394, upload-time = "2025-11-25T17:57:05.059Z" },
+ { url = "https://files.pythonhosted.org/packages/19/c7/41f4efca4abaf31bff5fa891ae0f594623539afd936abea51e0e546a6700/reachy_mini_motor_controller-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:602cf3294f54ceae4fa84d006aa0e97dcfcd99f7ee556cbbcf3546ac42fe8fb3", size = 399788, upload-time = "2025-11-25T17:56:59.383Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/0b/3b118d1ed20da22c3844c88247a78f6fdee679fff149d0e1ee64fc5d8aa8/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:aedcbebb270c3e6ad8898aac321f82a3d3060898ee33d75ae181b0f1df278f93", size = 581082, upload-time = "2025-11-25T17:56:28.189Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e7/c24da5194f16714d6f2a67fefaba817e2a22f68f58269500fa4495d6a29e/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d8d7626e3655a96266cc88d9ae1327c8b42512b61e8acaeb782d8d3352125c2", size = 567918, upload-time = "2025-11-25T17:56:21.842Z" },
+ { url = "https://files.pythonhosted.org/packages/89/69/cc9a51e04c1523213a8f4121bb1c454293d287f8c0fc4998e2640d053169/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8b27d6769f12a4bf75b8b6cb0e0dc20d154bc1422ff9e046f530434050552b86", size = 823910, upload-time = "2025-11-25T17:56:34.482Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/76/1f283234ec0fb1b349a24930485a9c584d94f064eb4d3d2e32e5607ec64e/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:07118c8cafed873541fb716928a3d4f807ac6ac7f769d7307faa4cd9bead6e6d", size = 910812, upload-time = "2025-11-25T17:56:40.804Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/22/771e3e02872c319f999c5e0830787c926d2b74b976c71f041d5429c4f785/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d552fdf890c3040232b2db783fc03ffcee230b633208e62b7a4265fa5d076598", size = 856433, upload-time = "2025-11-25T17:56:47.095Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/a3/c897ed4e7a43d205d43a985669dfbbc3943d1df8a101038c5384f34b9617/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b250a7763a72d6864ef4cdcec088d56e4d17947a10493f7347f2001502f05ed", size = 816617, upload-time = "2025-11-25T17:56:54.099Z" },
+ { url = "https://files.pythonhosted.org/packages/03/67/63dc13fe0fad1bafe12122b10479b2e22bb4562e58ed45e14f18b3f076c5/reachy_mini_motor_controller-1.4.2-cp314-cp314-win32.whl", hash = "sha256:d9a6be4852cb8cbec2e19d2037234dce39defce653f3af9d4a46cd56d624c617", size = 376862, upload-time = "2025-11-25T17:57:06.232Z" },
+ { url = "https://files.pythonhosted.org/packages/32/08/0c4b6fc4a761ad8bd362f571d3693c94aa0c15121e122f1c01e3f38bc873/reachy_mini_motor_controller-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:aa7b6ca1b6db09e6249fdb5a31376974e82928292950cd7df4480cbd5707a425", size = 400104, upload-time = "2025-11-25T17:57:00.616Z" },
+]
+
+[[package]]
+name = "reachy-mini-rust-kinematics"
+version = "1.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/82/485aa2f28973eee6198b2af0db9e76314fd909076f06782228fa686f0e3d/reachy_mini_rust_kinematics-1.0.2.tar.gz", hash = "sha256:ca14f9193c73a53612a32cd0bfb9abecb13343bc94fe5feb95f54736b7f3cd64", size = 17187, upload-time = "2025-10-22T08:35:50.104Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/22/e3733bb138a59790d4355ecefb7ca2fd5cd2cd6af05bf26fa7ad24b6ace5/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:0137084968573589d1958372e140097441e4a8562ac44b7b94407d2fd1b05aa0", size = 303531, upload-time = "2025-10-22T08:34:53.096Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/cf/1297187d79bc8268ca352702dac152a577ca96d25188767c1e4c43e51b5b/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c6457a28dd48a958d9ecf84a119736ae8cd0cfe7d002a518afe90ca8d4c1f4e", size = 279068, upload-time = "2025-10-22T08:34:43.331Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/d9/e62dff886e73ecce02ad81dcfaf95e315d32f9134cf1db14c78a0a8dd851/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76067037701db3e4ef9bb99dca34d06cf4ffc6952f63d05d815db090ebb49330", size = 312732, upload-time = "2025-10-22T08:34:01.097Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/27/6915e8c2e97cb97569742e9f77865b27887b3ad916d5b70c8c034785e2fe/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ee7cb4a450cabe1265974be1cbf784ee85bab605d05a3173fe290e008f166e8", size = 321001, upload-time = "2025-10-22T08:34:13.385Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/77/b6a7b46bc18f3c042d6591cdd0cf733ffbe938d73993ebf9e172105913cc/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d77ace8803a4fabbcf2696c31a9b2ac0136a3c9db83ac83613782934fc6c94d", size = 337679, upload-time = "2025-10-22T08:34:34.543Z" },
+ { url = "https://files.pythonhosted.org/packages/05/1d/ff2d5c5c9cc9f992983e07d455316e88f5ab2bea2f0859610c25737c0c87/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1515cc1028a472357cdf8005ee6a4e3ede919db6205af96bee215d6af967920", size = 351670, upload-time = "2025-10-22T08:34:24.985Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/4d/e3b34b00019d20b5dedd51e2a03500ea1fcb2bd2957b53ce730a9ac4c4cc/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40b03c1a09a3aef083df78bdc0b160f1b1e18cb660c7f134817b82b3d8a7de88", size = 493329, upload-time = "2025-10-22T08:34:59.959Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/75/acab41d132e7237bb06535f2b4a999f7a231fbee37ec368f7567454f73f9/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:5ff289e4df686ed764b16df1e67d9f94da90422c2784baf0502bf67bd89e27ac", size = 585106, upload-time = "2025-10-22T08:35:13.636Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/12/3eead1dd55587c3072827717ec28fbf029a5c8bf7bdf877a8a0dd1292123/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3b8c3af089ee5452f29912ec9e41840a7db99f96e482a7d837b8c21a2cdfc8cf", size = 526581, upload-time = "2025-10-22T08:35:26.295Z" },
+ { url = "https://files.pythonhosted.org/packages/47/62/5b6f069a373c2c16046094d5054b28932d932cf701675089caed3e7eb7f1/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e96d478a9acec92dc1c44e7b490b43e45cf182c327fde44f1e42ca377445aae2", size = 499068, upload-time = "2025-10-22T08:35:37.591Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/c2/fe92dbbee923d0b42fb2c44a6c9876490ef71cd6eb74fb63c73358f9726d/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-win32.whl", hash = "sha256:5c2f0c442c3b4f5a351c9e780b5886259c093a711579ea6e59f3658838450bec", size = 176754, upload-time = "2025-10-22T08:35:57.424Z" },
+ { url = "https://files.pythonhosted.org/packages/04/ab/c4a3541e681249deba417c7bbec819f4c44ab532bbc244f8e5cb6eea549d/reachy_mini_rust_kinematics-1.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:b3aef57e7ff74e5dac8db7e299f9c15c7a2e9934466ab2456d129bb8244edd08", size = 187313, upload-time = "2025-10-22T08:35:50.932Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/fd/c3801f0579da40147c7cadc1226bc3f5b7413cc590b13f53822674a910f7/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9fe481d3ae29415e9be3358493bd6d26add4b494650384c650181595da10204", size = 303080, upload-time = "2025-10-22T08:34:54.167Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/1b28508a05fde9bf80277222991273721462afa84a7a0ede6227692b6a75/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:58c87c6cb62a020f325caf8344f003ba4ebb783e20f417c6d89e3b63a72bc8a1", size = 278691, upload-time = "2025-10-22T08:34:44.295Z" },
+ { url = "https://files.pythonhosted.org/packages/de/22/9b4c4b007085db8fb329656146742414e64051d9deb8e108020fbb3aafa8/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af2f330760b5519dcf556c728ddaa889fbc1b5ef6d76a4f3ef2eee1ddd032cb4", size = 312649, upload-time = "2025-10-22T08:34:02.52Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/e7/b50b441d287e72864e32b1049ab6d740158a8330deba5ec2cfa62d5125e0/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8bffa448b0a75a570c5b8625344de3a755bca7f3a7450b045df153cef0cb402", size = 321102, upload-time = "2025-10-22T08:34:14.669Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/4b/cddf709a7012450d3255d7ab6ee6a1216e761664657ffe68806712d95c42/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ae0811ccc4132f9b034771af061c513bcb0bd18a73dad1d16abc6bad366caa", size = 337443, upload-time = "2025-10-22T08:34:35.544Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6d/908fca70c2dee3e066831fcde011fb0d8c563418c75e052208bc538105cb/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5777d235930be71186adf948388580317943a022aa3761d6f78bcffe7dfc5ee4", size = 351864, upload-time = "2025-10-22T08:34:26.064Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/e8/ddbfbc71cc2a7246de06cd4a0e934c9613a3a814406afbf8dfcd071813bf/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cbf226d07d34a6a395635b90ebb4d6e083136f07ca2e746d4431c44b00fd2d5", size = 493208, upload-time = "2025-10-22T08:35:01.445Z" },
+ { url = "https://files.pythonhosted.org/packages/75/d1/51af83f5b70c2eaf5f871efdf62c7d88116af2d5309898b2de9925857556/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bab5d550e2225c9d4d6ee69b98fd13cf75b8b3df4ab9824d7759668e04a6e5bf", size = 585005, upload-time = "2025-10-22T08:35:14.756Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/a8/cef7d415d864c8fa04362485ecb1648947a194b90426d147972ce59f4263/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7fc0243f9abc3d14aadaaa19b86d4930bf1e3bace0bfbbde73048ed80928b0f5", size = 526789, upload-time = "2025-10-22T08:35:27.367Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/4e/98e8e72f3cf0758eb4e6eb7320438dfb9e08d6ff593f6cc3661e726cf9a1/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a060b9022becd8decf9868063099fe68e598f7c07406d96795408ca862d208b3", size = 498731, upload-time = "2025-10-22T08:35:38.944Z" },
+ { url = "https://files.pythonhosted.org/packages/26/fc/d9b66b439ea27c20d01e6cc841b34b3a8cc17fb0e79acaaa8ba4f5a4c587/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win32.whl", hash = "sha256:ebf961d3ae3cd0798ed8d4e2bec8aec57f33fe2fb713852ad0ba2de4386c9c1a", size = 176607, upload-time = "2025-10-22T08:35:58.496Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/be/848776e83114ee92d9799860860be6dff036f22c9d01e85805c02a6d05fc/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:8afa30ed7da49544c8215033d2375c2f76f3311a6a1f380fe9acd2c076f9d45f", size = 187332, upload-time = "2025-10-22T08:35:51.994Z" },
+ { url = "https://files.pythonhosted.org/packages/11/39/6c40cca1970ce47d9ae1661c87230cd37a6e2d782f34e0aec745f155d831/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:356134c5df18a01226432fbd1fb24649aced5a14da3c0406ebe2d1b3b8c002d2", size = 299587, upload-time = "2025-10-22T08:34:55.208Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/30/f9ba417bc1782d6a256ef9024eb0329c50cb7a3d12e83afd4b67047a01d6/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a232408fb7c58d6da73c40a40a120bae4f51bf9119de108530572723a00280d7", size = 276656, upload-time = "2025-10-22T08:34:46.904Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e7/e3bb2597d86f2037a1e4537f2352d7e92ff4699ee725b43a90400f15894e/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e7c3d2c73f89bf817d8c3abb8f0b23a6efdfce330a749c6e29e1e0cf6db9d7d", size = 312798, upload-time = "2025-10-22T08:34:04.219Z" },
+ { url = "https://files.pythonhosted.org/packages/62/2f/64d2c4b434ef413c948935a0f57b4a502cc7effad68dd97263cdee22df64/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba0faba18be71b68026c70ccf4db6d9ea588774053d22487f228b490204a8a00", size = 321023, upload-time = "2025-10-22T08:34:15.628Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/0e/f4cbddd5c0ed10cdba7f4baffc68f2e8102eb1d55b309690a52af3b067f8/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bed2ec420ea354ce62572afe8672ae8701a139781d9d8da97932597907019ee", size = 337756, upload-time = "2025-10-22T08:34:36.66Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/d5/ac043e65949ba904b7ac901d74b8514ab47203de3af44e96c44dd2da7a94/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a79f6517728463294d5ca4945b18d843dd16cc410815ce039b35c1198bdc355d", size = 351251, upload-time = "2025-10-22T08:34:27.06Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ef/80bb31cee07d2336013808ce3b76dee29ae04274f053e1694e3a285da555/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5404cf14cc8894962dbbc82594af453588539b4b9458b8f95f43c29bc8bd2e34", size = 493292, upload-time = "2025-10-22T08:35:02.56Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/e2/4f6c5a0d2a77111a9806243160b1aa3531ad3024a59f05c7f6157917bcc5/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2e1f9d9ce4222d7ecda5cb77c96fed763de3ac94993e60c73adf9749d26e16df", size = 584893, upload-time = "2025-10-22T08:35:15.981Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/b6/b0b57c9c46b77aa3c03f4071fca0c92809e23f9b22df5f85446ec5329fe2/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7c29c3d7b70d0576c740969cfa3e35ba98cc8e378c0867a79f53d5fee3568f77", size = 525948, upload-time = "2025-10-22T08:35:28.451Z" },
+ { url = "https://files.pythonhosted.org/packages/06/12/f4d94e8c0b1b19b47072c2041f31fb83701cff1a199d0fcf012c0489f32f/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b94ca8e94058efcd89553144b0a0b0f7c4abfe67a4cd60e2993cd0833c9e130", size = 499239, upload-time = "2025-10-22T08:35:40.026Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b6/907870cb3f588a6da58b64357c03241a28dac8a9ec9f42e91c291cb95c51/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win32.whl", hash = "sha256:edcbcedaa4aa99684588bd897733a33ff8f8d9eed8ae39bd7d0fd691874f0457", size = 176379, upload-time = "2025-10-22T08:35:59.841Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/c0/a5c1db0bf8dc55cf6b41a11463285ef440a2efa74d8b75be8443234c1acf/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:eb5d6d27ce08e8816496b6049979cf64ffbf3901dd5bb290efe439f5455526c2", size = 187469, upload-time = "2025-10-22T08:35:53.307Z" },
+ { url = "https://files.pythonhosted.org/packages/13/5c/fffb02be21f666cf880042804c703de2410f32a44b0412d6df20bb156d15/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2aae2b9424a111935e26977fe2ab25c130c766e8049d72073e31a7ad78c84fb5", size = 299868, upload-time = "2025-10-22T08:34:56.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/33/b7e610c6325ed4fe439bd0be3e96850b5a5b6dc360116741bdf4c3a57ce9/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38ba383417c472ac67945a57c62ba621128b4486bd9d818512929454f47ce76d", size = 276655, upload-time = "2025-10-22T08:34:47.905Z" },
+ { url = "https://files.pythonhosted.org/packages/93/bf/a1f579e4a6930b2155c99f80ac5b96d256c0047537c0802c34f0adfcde81/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3788d9c5b5315eff026486c3367e97ea23cd4ccd3eb6c7c1e37e0d00ec6b7128", size = 312962, upload-time = "2025-10-22T08:34:05.504Z" },
+ { url = "https://files.pythonhosted.org/packages/89/cf/bf2a450adb2e6fbc5a9baa3ad9bece4d7272341723f4e76eff710463b484/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6983b555c1af6fd2c1ca8b32ae374c2b060239bb04b9b562806771ddafe81abf", size = 320871, upload-time = "2025-10-22T08:34:16.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/ad/1eab50e3c56a65ac2f14a4890ae5370984586f52adf13cd8a7963ebf423c/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca28a8cda1ab4973259ed9aa06c7173ac5899492a3140075c58225a0ed96be52", size = 337653, upload-time = "2025-10-22T08:34:37.679Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/ce/8d3802dde54f9595daa9bd7b12a3de42ba4b4b48d92d8abe7a43919d74a8/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c8591616ef354d4f0724affef58eb14374f4a06b649b7947e94b97ec22d2fb9", size = 351553, upload-time = "2025-10-22T08:34:28.126Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8c/5e1de72d044440afb8cb7480af0c9ddcdcd9654b0ac62c11113fedc731d4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45f4d9f159714e60eef22c9c00232beb34a8d27a89acce7d3ffcec2b86992b06", size = 493556, upload-time = "2025-10-22T08:35:03.963Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8b/1a33d934b9720586dc261349b500fb6a969ff1d3ba7c6f9bb7ba87d61d37/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:02a525696238dc85e9a1f3260d7d0db25074bb54f1cc2a0a4052849344026806", size = 585019, upload-time = "2025-10-22T08:35:17.344Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/5e/8cf4011456e3262f2a67b66ee77838cc56655cdcd7941efd44cb6ff8eb85/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:342b4a9adb9714f98d6569121c053800ae0a5b3904a9863ca65603bb3b9e7f0a", size = 526143, upload-time = "2025-10-22T08:35:29.636Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/3e/0d1168324c288693c07abd0050b83f682cf12a27c850e42d2601d3da84ef/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b596910a1ba0aaf79fc525ec5a8405dcd41c28946667faa549e789e5c1b93a5", size = 499284, upload-time = "2025-10-22T08:35:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/66/4e/0f69c0287a9289a0bc78d623cbf8cbd652e54ff6e854979b4985eb7fa75e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win32.whl", hash = "sha256:d5437b5d7f9093e879859d83da6888738f281fe728fad0a9f1678fe01035b4d3", size = 176631, upload-time = "2025-10-22T08:36:00.974Z" },
+ { url = "https://files.pythonhosted.org/packages/94/00/eff3fa6480da3c2b486b8bcec55ea650b4955fb874664e9e7dce205a9885/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:c3c813012eacc7e779c81388f3dcb30811bee461672f9f9dc03835c73e3a5bc4", size = 187334, upload-time = "2025-10-22T08:35:54.464Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/e2/2c19fbf08a5e3836147ee345b9d75fbe1cd26df8d1f9e83b9cefa4bc80ae/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e1f563b31cbb0e0ee9f015801ef2eba31b9c37c306e3ca7737097a482355e0a", size = 312043, upload-time = "2025-10-22T08:34:06.505Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d4/873c4317a598e9709dd2aa68f0a072568b1f2db3616a2ffa76259618889e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:874b986c44a94e1e1752f8f0b159342b0f60d4610e4df9c4bdf421e2749d949e", size = 320190, upload-time = "2025-10-22T08:34:17.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/11/64941229b39bfb1c39ea2ccb5fd02998915f87e40e277e4b0b744a2c3a2b/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f5060865aba96d3ffe1ac5cb93dc6439154a78ef8fdbb8a4313e4dc33064efb5", size = 492437, upload-time = "2025-10-22T08:35:05.014Z" },
+ { url = "https://files.pythonhosted.org/packages/20/47/82704725e0d5cae887a4604e9bbf7688e6a676768ab47202b018ef9f7f32/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f7ddeef8610a936afbfd7dda1713be18d207032e18604e46963729996c3d1a41", size = 583980, upload-time = "2025-10-22T08:35:18.517Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6d/bfcdc6912531c62885450646f629aa581fb116ee5806dba00c01712b02f4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2f498398c933e87a63a5747a2c813df09dcb68ded3ba79f86d092801ced36da8", size = 525181, upload-time = "2025-10-22T08:35:30.819Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/6d/4f32bc65f83307a6eba5b62231e276bde8509d26dd8ba1a9997fb6c4b924/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6464868b209ec5cc0e50829b25bdad8cf765a48c943a5a7f504a3ec4ed7aedcb", size = 498743, upload-time = "2025-10-22T08:35:42.183Z" },
+ { url = "https://files.pythonhosted.org/packages/85/26/2af509eb72c556a5f8628454122f61bcd55ae760ed74c2a1d8cfe21ebafe/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:419cba0b2ca01b52aba79004681201e1ad16d5a06a2ae9f4f74c45ea82e88f11", size = 276979, upload-time = "2025-10-22T08:34:49.023Z" },
+ { url = "https://files.pythonhosted.org/packages/82/62/5fdf253c4293447fe2b95329891a46292fed02fd15f71a7eafbcca842194/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c654f6e06da8c6f8f5f2613c7a8d1841dca730ba2397125418f1b26bfeb46330", size = 337650, upload-time = "2025-10-22T08:34:38.745Z" },
+ { url = "https://files.pythonhosted.org/packages/01/2e/eee9bed34de89569f52965fdbf6bd16c914c1b87f4f233e49ffa1f3f0590/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:58a36591b27a80c0836ab08ff21bf605413213f079baeef6fd1e3d6b47d8a341", size = 352082, upload-time = "2025-10-22T08:34:29.451Z" },
+ { url = "https://files.pythonhosted.org/packages/06/b2/bf657750a75dfc8b5e12c1f4ca2e09ed4c4c7ea207295fc82a71e34dfad1/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260bef2b277a63f5ded78b03cdcb98127862ae8eb6c4794ea25a71be5a03cd53", size = 313219, upload-time = "2025-10-22T08:34:10.265Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/3d/df94e31f27e1f5e725ae188c6804e9064a78be6a3d052b3a2146857afd5a/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2f73d69afbb6ddf4a0cf24e557a6340e71777a2b6de2ca4ac3243bd3f8abd5", size = 321182, upload-time = "2025-10-22T08:34:21.259Z" },
+ { url = "https://files.pythonhosted.org/packages/19/94/35c9fc05d387026594f576b514d964af0a69e2b00712069215a9a735365b/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:111de74d29630f1a2d8d0282328764bc490cb4aa63270b4f58467e9766225bde", size = 493659, upload-time = "2025-10-22T08:35:09.472Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/11/505834201862263b5d61beca2bbd43300f669a5960d482edf44ebb8cf835/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:3126d71e7bdaa180eb62ef97cfd56cdf3b21c0f0693535ca44ff2ee12caea75b", size = 585112, upload-time = "2025-10-22T08:35:22.295Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/d9/eb0ec95412262132dd88494f85ec40b58f2ae6c83e4908585746edbad3c7/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:89735b9ddb19eda38f4a2ebf1c7d7dbddbc9b39f55d407e87db5e151bbbca4b1", size = 526493, upload-time = "2025-10-22T08:35:34.19Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/0b/e403e4ce2d3ffe1d97efc54a5ddb024b1fff6c6f8607a1a306f83a615b47/reachy_mini_rust_kinematics-1.0.2-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:58e4edeec417a64c2508d25ce57a45932253d2339035bd66388a54beaaa91afb", size = 499556, upload-time = "2025-10-22T08:35:46.538Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/2d/c4492e19625f81348c812e5e17e97e777c521fe7200006bc41ebc904dd14/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305793e3097db1a93eabeeb5bcf7a581dedb859740d5f5164d7f2b695ce90953", size = 312932, upload-time = "2025-10-22T08:34:11.265Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/b2/d6cdc7960650c609b3e902734dcd10d6eb44373ba4f0aadaefde401244ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dda63846d7196737bad42d9de847b3c4702e68e40fc0a691fed50a87b9ef0898", size = 321183, upload-time = "2025-10-22T08:34:22.468Z" },
+ { url = "https://files.pythonhosted.org/packages/51/74/d92143a61237d3f83ba9f6398f7d546cdefd8d43f820677bcf4d7ed3a265/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b307111f6ff46a1e8dc5b0d600c836ccf237106cf0bb9b87d85e079199fb733", size = 337577, upload-time = "2025-10-22T08:34:42.092Z" },
+ { url = "https://files.pythonhosted.org/packages/05/e4/5ab1e64bd201e388f3fc0bb1ee89ef166479c8482d582ee58d603ba1b6ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64649a8ca74f3f528d57adf02f3b5ad6eaab4e5ab40bf837554ad878f0f4ea64", size = 351547, upload-time = "2025-10-22T08:34:33.109Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/19/f754c4cac653d3133145694abb6bb2d0da15b2459c1361b35528b3d16287/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6f0eda0e6cb8d86e80937a78b44e3db5e338039480fc077503aca3c23af008e6", size = 493554, upload-time = "2025-10-22T08:35:10.535Z" },
+ { url = "https://files.pythonhosted.org/packages/af/81/14e1848dc1fb662a9dd8108bb3be4161a241055d38bac65eb9ee1fe5926c/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:12b9c538544352ee829338b3ae34623e3903294cd09ebb286f9467cad9882360", size = 585142, upload-time = "2025-10-22T08:35:23.413Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/47/a74608d37b58a162a3204ad4a1b7981ca79dc598b6c76533922cdc2bcb48/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:56eb81ad90e2b04506b4adf809b48a449fbd3972e2b32bd49038b745a6ff2d38", size = 526427, upload-time = "2025-10-22T08:35:35.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/8e/c48f621c8d954849deb081a7a8840c57e38385dad999aaf5b82e10a09f84/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2af9062ffb5970b3318a85eaa7ce78042e7b023e7c096e34002da5aa083920dd", size = 499551, upload-time = "2025-10-22T08:35:47.616Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "rerun-sdk"
+version = "0.27.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "numpy" },
+ { name = "pillow" },
+ { name = "pyarrow" },
+ { name = "typing-extensions" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7d/ef/38ecd7fbb31b846d749299c2b848a30e85b7a6a7d496ea054373abfb3b08/rerun_sdk-0.27.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e3e180a97085e7d7e1f1aee20c45f96173c87d723ce155573b96441733ef1560", size = 99965645, upload-time = "2025-11-14T14:49:18.53Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/67/ce06267c5fc5c7bb1cc027410a75cca0c5232afa1d4072ff2abe685cb923/rerun_sdk-0.27.2-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:224d168b9673bf9237057db6beb62a3a63223d702602c2e57283089ed77acf42", size = 107368188, upload-time = "2025-11-14T14:49:24.538Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/6e/0be74188a53af91572b466e6f672dae476b9058ca71c7d053e753a665087/rerun_sdk-0.27.2-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5583f9d67b485e405939ddb1f9da38cc7202b326a15b1110d71dc7d2fdacc28b", size = 114630744, upload-time = "2025-11-14T14:49:30.28Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/8b/9fcb34b9d7bde9183372cc3dbaaca8d2ce49a43015e41dfe35bb62dfbef7/rerun_sdk-0.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:d0437b73a807bb4643d3a7fee163e65e0df0459e6228eb2b087692610612ce99", size = 97879453, upload-time = "2025-11-14T14:49:35.062Z" },
+]
+
+[[package]]
+name = "rhoban-cmeel-jsoncpp"
+version = "1.9.4.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cmeel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/0d/8686269eecbea32f314865075521b6305df709fe6bf0793397e31ef7506a/rhoban_cmeel_jsoncpp-1.9.4.9.tar.gz", hash = "sha256:953003fd1ce3b447170ef21747180f70a50ec2e0b81c6e193088adc9a90d37da", size = 220876, upload-time = "2025-06-24T18:54:10.885Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3d/8c/6006d71a441c2e51427ae2016ef8b227a78f9074717be46fbe2b196c805b/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0c032d05ce19aed18bf6b454daa510d4dcd89366745e7bfd062a6a16c3bfd51", size = 289726, upload-time = "2025-06-24T18:53:06.57Z" },
+ { url = "https://files.pythonhosted.org/packages/38/fe/080a251ef48c99e1bd272fae8442a65864b7ddba94cd207774e6c84e659d/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b606a2bed978408825be84a4731bed3a10b7f91e6dbeb5dca2b0955ed5453a3", size = 281613, upload-time = "2025-06-24T18:53:07.909Z" },
+ { url = "https://files.pythonhosted.org/packages/91/18/f035f099a24bbe0b468387c5eede093ba4ac1636c5b5ef8bcf0c946fba80/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-manylinux_2_17_i686.whl", hash = "sha256:d29850410ee68a3201bc10ec0b0008afd9c7ce70888355fdac14a52efba48baa", size = 491818, upload-time = "2025-06-24T18:53:09.208Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/f0/513b12ba87a62272adff2298f22d78185faf54265afc298f1d8c337e7a5f/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:41cc0235e0e1132f3332bf0b8ca49b6e01693b04f56110633976dbd9dc33fa57", size = 330615, upload-time = "2025-06-24T18:53:10.758Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/a0/79a987ed722365b61051da30484cc7e8c8837b0e4693149b96c0173287ab/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e57547e0d34e18f989ee4966380deb48245d46dc0cc4da28ebaa7641e6615e2a", size = 362586, upload-time = "2025-06-24T18:53:11.854Z" },
+ { url = "https://files.pythonhosted.org/packages/07/4b/035c9def4cca5e62b3053e8f728f500e0025e8e0a4c5c2fc70a9b5a065d0/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bf644a2e17f88f509c28e58c0d318a94c3fbae1c09eecf447b1be98f97a42f44", size = 338462, upload-time = "2025-06-24T18:53:13.343Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/d7/12e5dcc03c73d13ba546f522481d944f44829758eb6554b4751f49d45b0b/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ff4b6f7a26c89a2aed2242c6b834fc8abdfc099692b683e972f6fc4ed8ad83a", size = 397780, upload-time = "2025-06-24T18:53:14.836Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ae/cf627de3c52a80c65163fd3e214c3f0326d0411f67eb64db6629bc71717b/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bc0bb9ff7d0090cf722c72ef4088967336b6dd304fc34db6787345edc16f3be4", size = 363910, upload-time = "2025-06-24T18:53:16.033Z" },
+ { url = "https://files.pythonhosted.org/packages/87/63/51136e4fdcaedcbf326fc242753e2291a5e3aeabdc0c0d5a5bab0edc5378/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b3a0fd46c2e2e6fc6912dcae7cb987cd0a61d9caee4f3ca1cb62544eb8aa11a", size = 289728, upload-time = "2025-06-24T18:53:17.427Z" },
+ { url = "https://files.pythonhosted.org/packages/77/ee/bc97fd2474f0f28449149211776ac63b9d76a82f7468277e09611e9253cf/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffd64a71c7e74312180c4ff20c55f0ebc235c3d634dba18618063c60175cd06c", size = 281614, upload-time = "2025-06-24T18:53:19.575Z" },
+ { url = "https://files.pythonhosted.org/packages/82/a6/5cb9320ffce6ee8f5a62f942aabb7253d03ed38f611d891114a5c1304b7a/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-manylinux_2_17_i686.whl", hash = "sha256:6f4c09ca5ba465769e972f94bd8113927aed0a30932caefc7799dd6b6a5d83fb", size = 491819, upload-time = "2025-06-24T18:53:20.751Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/ef/df1cba0fdbe278b45e2eaf617b4aedb29b7670e04795b1dafa5339ad67e9/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:cfa5a8f18c3fe4a973ff1e49fe5f1048a81b2f2cc1d22df0770f3d23a91d455c", size = 330615, upload-time = "2025-06-24T18:53:22.243Z" },
+ { url = "https://files.pythonhosted.org/packages/54/ca/fca12118933539461dc66cd70fd2dfc38f0dff99075e544ff9afb3924479/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:db51d1b31d0d0fe04e5d7fc123a849154689cadc5e43a289c20c8ecf243c5fa5", size = 362584, upload-time = "2025-06-24T18:53:23.831Z" },
+ { url = "https://files.pythonhosted.org/packages/24/2f/9829e23758b6df6ade5da3f7a2348adce273c271b386573e15cce201ba88/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b885cc44fd59e2070b77f5463324683b3d99035307dc0c9635cdbe4fc1759741", size = 338463, upload-time = "2025-06-24T18:53:24.986Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/dd/a5adb0bea0bc8ca19b3631e2d4a66e8b2a66b4497aa24f176e8549b3c4c2/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:23d94e38b1266ad987ed42369841839d7299b6e5745890f39882236485b66e08", size = 397781, upload-time = "2025-06-24T18:53:26.633Z" },
+ { url = "https://files.pythonhosted.org/packages/37/7f/8f9d90373411e297db923bf5c42c596fc4bc6f4955561aec508fa375b299/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b67cd1ff1d3801154e0ca6518f77dae6d75297f83de8246abb20cd8f0813d6bd", size = 363909, upload-time = "2025-06-24T18:53:27.796Z" },
+ { url = "https://files.pythonhosted.org/packages/34/c7/70335d8227dbfe222a2362eece6a37ed6526438636d445fa53cee897625b/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f0f62d47631ed02be9a83669b24ed2fb870c27e39311b5ff4842473aa61e3ca8", size = 290015, upload-time = "2025-06-24T18:53:28.982Z" },
+ { url = "https://files.pythonhosted.org/packages/55/8f/c07df2a93cb2184f2b3cd4edf7ca9c4467d17466cae5dc84166de5365963/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78d34f04880cc0ff799832c3f1e2c34f7179c54ac7c428339f35c87e3bb5739b", size = 281613, upload-time = "2025-06-24T18:53:30.392Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/04/a7b6df62549654b940dd4737384d2bd15afc570595a1a98fbdcf8aa5fcbf/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-manylinux_2_17_i686.whl", hash = "sha256:3841bb7fed9ca370631bafc44c493b1b4044048e857e1265b24ded9cd6bef385", size = 491819, upload-time = "2025-06-24T18:53:31.59Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/7a/ec81041d3bcd1c99ae4ef14505d81cbe8a424d1bcee752bd1f32b6391d24/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a68789f5226bb8df92383140f3f45b94afe07b115fcc3b34a0e1da806d7d1f66", size = 330613, upload-time = "2025-06-24T18:53:32.985Z" },
+ { url = "https://files.pythonhosted.org/packages/09/5a/96550200a9aefea3e14f1eaea0d62a649968883bb1c927509666d2ad680c/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a3aa7cc485acf11e4876f0ac20ee5ffcc0c89c9dd7b8eee4d5e078392421e2fa", size = 362585, upload-time = "2025-06-24T18:53:34.14Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/e9/1c32193f9a2e0b397417b20877f0c3fd2b3b88a20b85b4f8925ef5e50dfe/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fbcda72ec3aa1935b355df5dbde55f885b3a7aadc7d9b8d1ff1cd274247c9a15", size = 338462, upload-time = "2025-06-24T18:53:35.297Z" },
+ { url = "https://files.pythonhosted.org/packages/72/6b/bb9e6e6dee18a32f47213c42f03291901d6214d3a352c7104304e06d684e/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2472911f555b8f39caaddf23601a08befdf218e08c521049b0cbd705c1693772", size = 397779, upload-time = "2025-06-24T18:53:36.413Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ca/5aa50bd53bad9c079e2ceabe17231803c420fcc549894e77c7266f1b3853/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cb67950a3d3b4a6ec23fcce2e7f844c2ddd1c626ff9f4c7991c699d66213be5", size = 363910, upload-time = "2025-06-24T18:53:37.595Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/17/43932dd90f1febb6cbc6c8d17df5b0b9f0bd3b21660c4fb3d846770964ee/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:096d147389647ee103066e91b799ec230842ebd2916e0a59069bf598f06020a5", size = 290015, upload-time = "2025-06-24T18:53:39.01Z" },
+ { url = "https://files.pythonhosted.org/packages/78/a1/3bb178bdb062bab896aaa5bae91e43a83cf986a155dee1479a27a289fad3/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a3f54fe0ea0aaa91c46dba6939a822f08f8f5a2611e17074e9b4cf0273f8ca03", size = 281613, upload-time = "2025-06-24T18:53:40.14Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/3e/f8cc82bf48d1c638be0c17a70c6b8a2d84f55e6c84adae88074d30e2d3a8/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-manylinux_2_17_i686.whl", hash = "sha256:f491c23be755f33d04fcd120b2555084bb2b5f8ce6610853f55d0b7ef73d9454", size = 491818, upload-time = "2025-06-24T18:53:41.584Z" },
+ { url = "https://files.pythonhosted.org/packages/71/7d/b28c58560b757a1d2e821ddfab9a737f760573dc331d604d2f1a50ad0c59/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5cfa7582e21289214dc6e0eb17e821a463429f4eb9bbe0b1d73c8b6b5070e822", size = 330615, upload-time = "2025-06-24T18:53:42.797Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/45/c0a97f844b416ac4bb0701cb5bedcdd714236c60a968f1a17a929acf2030/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0e736f7c65279767558477c73b7ee07d7900878b6489362af90b75bf58ef98e5", size = 362585, upload-time = "2025-06-24T18:53:43.976Z" },
+ { url = "https://files.pythonhosted.org/packages/82/d1/e68942c2136ba7f16150d7174376df83f7b5034391605839a280c94b6b93/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2d289decd44461a22561353d26a4ebd94a35d7967de43a094cdf1d68afd47ce", size = 338462, upload-time = "2025-06-24T18:53:45.242Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/5b/97acff8d776ef2fca5d4e21893624dbb424095f1a4b66c3c0d1375854341/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fe1165ea4390a4cfce698f1018b3305e27abf4f9db7f464116f7252ae27d16ae", size = 397779, upload-time = "2025-06-24T18:53:46.76Z" },
+ { url = "https://files.pythonhosted.org/packages/11/7c/4f2cd6df47c6b83652e8f9ffafb25f056a1b01fa0050df4b4abf5f41dc12/rhoban_cmeel_jsoncpp-1.9.4.9-0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f49041be98e0104856f8a0874936f50be435c514a8cd20ed5291b82d81912099", size = 363908, upload-time = "2025-06-24T18:53:48.05Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.12.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" },
+ { url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" },
+ { url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" },
+ { url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" },
+ { url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" },
+ { url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" },
+ { url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" },
+ { url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" },
+ { url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" },
+]
+
+[[package]]
+name = "rustypot"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/41/ad/22e84d4b3099e3fc109fd65e3af9629987974a227fa566d229c32a0bf654/rustypot-1.4.0.tar.gz", hash = "sha256:5e8607f0cff9f2953f59c567e34cc089f4fd9ff88ebbb41d4b329dd9dffcf0f8", size = 60097, upload-time = "2025-11-19T11:53:43.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cd/ab/5e24e49b1472a7ff97a16a3a7606ea29f2feeef018fc404715e510156e2e/rustypot-1.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6b99e6e89028116e9923375c2aaab1b8da8cd5c1dc2dcc682a893765517c7f0d", size = 1657408, upload-time = "2025-11-19T11:53:17.917Z" },
+ { url = "https://files.pythonhosted.org/packages/15/af/9208b1c1e350b1ecd986e6782d64b9f8f770e3da65f3707952501db3b56a/rustypot-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91dbf14f0f0e31969df43185bd18cfd6bc7341e6fad815d000175fd5cb1bd7ad", size = 1530195, upload-time = "2025-11-19T11:53:12.142Z" },
+ { url = "https://files.pythonhosted.org/packages/02/db/c511c59b0c2428eaa59f2cf3640c677dad83eee5e8f4caf876ebdeb3db09/rustypot-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfb9bfce1adea617eb948d5cf410703070ab7aac3b454014e172acbd4c4c1b81", size = 1673673, upload-time = "2025-11-19T11:52:49.553Z" },
+ { url = "https://files.pythonhosted.org/packages/82/ac/a7188231bd853be75388eba1f2b6a24922942c9f7a0de0a962e752faa8ca/rustypot-1.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd9896ce88eebbed3f9cd8d98774505cafd23b399256a3aeb3844b2ef1ced49f", size = 1726837, upload-time = "2025-11-19T11:52:54.942Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/de/97f330269bcaaccd81b1e1eef2c9410d9e13c16de97450c6b1632400b474/rustypot-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8c7f4709fc8f9338199ce0560de230a644f5e7c161f8ac3c1c24530d5abbcc8", size = 1752455, upload-time = "2025-11-19T11:53:06.014Z" },
+ { url = "https://files.pythonhosted.org/packages/81/08/480833485a2d960cf998fda6567b919258e08346309c1afca44d407cad97/rustypot-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ecaaf5b555b11db3132c803b79e93ea7db9caa5d08dc6bd67c654f29eb5bb46", size = 1737355, upload-time = "2025-11-19T11:53:00.727Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/50/e428c68fedf5c5ac78aebe321ed8ec84e247ae044b3306b36f8c6a74ff8f/rustypot-1.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f04aaa90dfc2b6c535e8b0897b844200dfc8d267034006a5d0f8bfc8329bb67", size = 1858991, upload-time = "2025-11-19T11:53:23.014Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/06/1fd6106dfb9eb1f8d0bdf5ef1ccb4d7e38db8d41fe0984201d67dde7e018/rustypot-1.4.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2800cd8b732ae43eb8936d3462c489c2efff43ded92da3612828debbcbd5515", size = 1996042, upload-time = "2025-11-19T11:53:28.339Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f3/d40eb22fbab02986bd5d8a6f2c86bae20425577fdb19b3ebbc0cdea62ae4/rustypot-1.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:80626bbeaf05ea3edd112dad541eda2f1771a252c455d381956fa77c95c1e6c0", size = 1823848, upload-time = "2025-11-19T11:53:33.327Z" },
+ { url = "https://files.pythonhosted.org/packages/98/7e/620c6b4ef86a83fe42d3bc3d18265ca6c53a3f644e155653ad18df93e215/rustypot-1.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8766222a6ac4df8f8afedcc6c2cf493cc71bf245e4b593e24fad0a61cf83e3dc", size = 1944672, upload-time = "2025-11-19T11:53:38.067Z" },
+ { url = "https://files.pythonhosted.org/packages/89/91/2c399e66eb3507a9b1ad8ec2be9695bfda7fb9a7305f787113cf95166770/rustypot-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:a162451a69766702a5a36c8055a822d5b7af99420ee131162d93c5926e78d7e7", size = 1671429, upload-time = "2025-11-19T11:53:43.789Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/4d/28a3f898567587b1ae6c40d26ec6e9d52d823ad09a3a1dceea71cc2840d2/rustypot-1.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5eb56ef3f4c93c0564d4387e51dabf024c392597bfc101b7d9bfbfa282b936a3", size = 1657433, upload-time = "2025-11-19T11:53:19.191Z" },
+ { url = "https://files.pythonhosted.org/packages/15/b8/c27afb0203b0d24930dcb673aacbf1db187d3d5d961401bf5596d95164dd/rustypot-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e84c3c06bc25511fec1c475985a6038ad5e1c77574ced3cdcd3b18bec59f3f0c", size = 1530013, upload-time = "2025-11-19T11:53:13.252Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/eb/60e6a2b63076a86f1c5fe09edc996439dedf4027bd900290019ec506a61b/rustypot-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:852309d8c8af86eafc1c4afd065c219f0ff0f0db19d486a7cb30cdae3bcc1e9d", size = 1673985, upload-time = "2025-11-19T11:52:50.843Z" },
+ { url = "https://files.pythonhosted.org/packages/01/0f/61d7f782b9473864eaf46d608f06e3425acbb72d31252a281baec34349a3/rustypot-1.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed87d23cb5b0bdb3f12539de9e71f7ba55d6ffb42a69d99082348d357381dc66", size = 1727323, upload-time = "2025-11-19T11:52:56.581Z" },
+ { url = "https://files.pythonhosted.org/packages/11/f3/042cb6871a464c251e403f85e31af5a387b68f290c6f7686891af955aaec/rustypot-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:042ad6d0e72db7036fc472c0de9be9ee6b035cee234ebcf9b10d75a30618c1c9", size = 1752098, upload-time = "2025-11-19T11:53:07.118Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/d9/47c8f992e0a1d7d30de17459724a4c736751b9c53c69b8c04e2942cd7081/rustypot-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:26e243a81cedbbe51abf461ca5ec2b9641730340b03ad52f7fd638d922ea300d", size = 1737415, upload-time = "2025-11-19T11:53:01.906Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/67/a1745fee19e4e025d51b5ee0eac13a20bf9984792e8dcdc0173bd3a99348/rustypot-1.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76e9fd0376815c227c915372e34a61366365faf3472f849e3d31de690eef9532", size = 1859533, upload-time = "2025-11-19T11:53:24.437Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8f/2e5db416b6b9428d99888baa2f045faf3fa0662e6d1ea10c7b3b3871f199/rustypot-1.4.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ecabfa2b34e9821ded4a686df16092530b233ddf560e5920ca11e4ffb3a9c59a", size = 1997475, upload-time = "2025-11-19T11:53:29.845Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/69/50de9222c02377ad7e2b0353034dfb04efa644bd220b402a6ea72da78a2c/rustypot-1.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6259a0e202f9d543d329fad83c3f2d9439061f17c9d7f08a5b02cc6123b807a9", size = 1822346, upload-time = "2025-11-19T11:53:34.654Z" },
+ { url = "https://files.pythonhosted.org/packages/11/df/da8a96db6d02a35c94c159eb2ff3389d9de5e2ee2beda04df4dce384a2ed/rustypot-1.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:883a95f573ad23a630591428cd8b4228c5bd62884323a079bf6517c588812f90", size = 1943995, upload-time = "2025-11-19T11:53:39.288Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/61/c108d25e61cdcb2f6aea984cbbf359952d935ae99115c7746e8dc86371f2/rustypot-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:09858b97b004337b8370c0a41b7e08a74c92e1d7530c9bd9106cf9037c090d69", size = 1671721, upload-time = "2025-11-19T11:53:44.918Z" },
+ { url = "https://files.pythonhosted.org/packages/47/db/0a1ed93069a7ab86d28fc4c47cb9e7a995211ac5d5d77ec1b5d68ae0e64c/rustypot-1.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c3c133ca8d9a7f0453532bec8d0f9c6f20e64f71d1141a25d9bc31e62b7251b6", size = 1651633, upload-time = "2025-11-19T11:53:20.422Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/6e/32fd0e56dbf6dcb29a2d2da8b4692a15beb63557a655e3e22f367cf19db6/rustypot-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12942dc5694d2511e12e5d59ea50a41da0bfab29d4cdd6f4718b05874d132145", size = 1517305, upload-time = "2025-11-19T11:53:14.311Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/67/4616ac267267ed9b2bbd82bfdce9e7edadeef5d09f7c63df737b870479d5/rustypot-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c9962e22adabe744e54b3699cd64099320a74b7ca7b1ed0be425b3b379636e3", size = 1643720, upload-time = "2025-11-19T11:52:52.202Z" },
+ { url = "https://files.pythonhosted.org/packages/17/95/9d77beeaf20c2bd13107d84249be010f951e5041e8045fbdac4f80e3d80b/rustypot-1.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:67a5d8c04e19022dbfbf6b38a886420b1928d7024d97767bfe5574b7f1d13e74", size = 1694743, upload-time = "2025-11-19T11:52:57.873Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/0d/2ce9f220ec672a847fd76e08831d9d17c57a59ba5aa1940951d96dfd61d8/rustypot-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c4cbbc072df203697ac5f2346be94a149a792e0ca052b6392504b27cfe2d447", size = 1745176, upload-time = "2025-11-19T11:53:08.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/33/3fe3be1f95998113926aa38532adeb9964a02511a0071d1680850cfaf74b/rustypot-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d73802eec8dc8d47555f05fa7165b87c33e9d3997c841213759cbe72bffa145e", size = 1699759, upload-time = "2025-11-19T11:53:03.064Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ba/ef2428a865b5350c649c67573519e7052756286913cd7d5b28a762e063e0/rustypot-1.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8bf4d6b87add0b275e993748c825751c0d7fcd77b8081e2ff344037bd13f521", size = 1829416, upload-time = "2025-11-19T11:53:25.8Z" },
+ { url = "https://files.pythonhosted.org/packages/71/67/b6dae95ec2d169bb8afa489418fe64623fe35b48f57fba6ba7649bdfaaa1/rustypot-1.4.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4b068f9229a329dbe327feb982f683209900739e2811d60e41a6999eb46fa627", size = 1963364, upload-time = "2025-11-19T11:53:30.972Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a2/946cde0c5d31f7ce7b7c79f79a1785f194b2922d2b4a67c53e6e1b8099e9/rustypot-1.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:519a3ba8bd2d9291436ef2259869ef2c918a9410ac063e33e76178d52e0230e3", size = 1815541, upload-time = "2025-11-19T11:53:35.773Z" },
+ { url = "https://files.pythonhosted.org/packages/13/7f/acebd54c52c7ecd0f4d2caaee0b4ea57047443d16cb64c2d9e9e0b359832/rustypot-1.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba4d42d5dc32e00dbae1eb592eb0a94d8cc8551c1b2285be774e4d9cdfb064d9", size = 1936044, upload-time = "2025-11-19T11:53:40.782Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/3e/877cb56347550409a9f33e92e224b7527684d89b4159a96d1e9f3065fe28/rustypot-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:acdc44bf55ece4d7205c5f8d68faaea1c7e16f66631d4533929e7c027c7d9583", size = 1671794, upload-time = "2025-11-19T11:53:46.089Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/cc/5592dcc870a6ac0c8ff3173c84985da9ebfe039a6c704387cf84247e3c6e/rustypot-1.4.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6a34cee68caac4dda00ad23dba2fb923dfe47ad8b82816bcd68fd742508dc772", size = 1650940, upload-time = "2025-11-19T11:53:21.857Z" },
+ { url = "https://files.pythonhosted.org/packages/96/8f/25b99ff877c82679c9bb6d54c2dd74d72ccf731565022f67ecb6a8fc6e93/rustypot-1.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c05c4500037d6c981324cfb560f3f0b2f9066ac725912de7dbbc22652a8b1a89", size = 1516899, upload-time = "2025-11-19T11:53:15.493Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/60/4cc071a6b81c0b7470234fd31dae86aa3a98326ade39ff8e1200a563d72e/rustypot-1.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe08c3535ef28a65c1fd3fa2f4074dd568b080b468d84c7cfdd6ac47bf3568a", size = 1644159, upload-time = "2025-11-19T11:52:53.521Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/6d/ea3778c7673559316361d1c021f6e774e3df449e928416bbe434721cdfa9/rustypot-1.4.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e222c615a4e859363a9d75f25e9394fe8e3d9634465e7244861cf9dd9568c06", size = 1694143, upload-time = "2025-11-19T11:52:59.329Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/f1/cc231247f54d38f8ee0c3c41f768fc2fa1a9c6a782f4c3928ea775af759c/rustypot-1.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377017dc152f75cf9a73ae6cbe4dae501051bcd2269d345b3f52d526af3388e3", size = 1744386, upload-time = "2025-11-19T11:53:09.629Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/c8/c137ba5b77413165d5a0e27b1937b10971e200984c0604b9ce751911f14e/rustypot-1.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c4a89781b13805d09f03a51665bb41fe6d41a3da466bc71fce7688f5865c7ce", size = 1701060, upload-time = "2025-11-19T11:53:04.548Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e5/a09da32532b5b2416e5faf1708d4a09f25682b984826f8e6b09310ed6d56/rustypot-1.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:624e909936934412ac068601a251e44749ab2acc02fac09eefa75d8b9d88cfb3", size = 1829242, upload-time = "2025-11-19T11:53:27.156Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/f9/69214ea8d15755c2a1a7a2a5575b7f675944b09a03abb04e5a063023b7a0/rustypot-1.4.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3a4fbc587099a96806f30cd15cc3ad20fdfca698083253f29724a516b068fd65", size = 1963269, upload-time = "2025-11-19T11:53:32.193Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c8/da7ba34a1ff0c7b7a98dd281f2900f7c1a9d6e8ffc2935b10d7e74d7cf0d/rustypot-1.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bfc0c8026fa00d00307cbad2e106ba36abb6022884365db0400147786b0c9f07", size = 1816497, upload-time = "2025-11-19T11:53:36.956Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/8f/07d9e529cdb9f8c7734f76af1f17f907da770025c7b2b904286cde6089ee/rustypot-1.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c453fb38f4435918c762513a91d89a586a415f6e420f79ab45016b643f5b4db4", size = 1935302, upload-time = "2025-11-19T11:53:41.917Z" },
+ { url = "https://files.pythonhosted.org/packages/35/8b/e7c248f46bcd3d9f5d974253b9c1e967ae010897da5af72a831535ae41b5/rustypot-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:52a5a208030f956fa079ad4405d24c2215ed6afd1a8bcc6e2a1ee95fafee67b7", size = 1671632, upload-time = "2025-11-19T11:53:47.224Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/45/36a3ccfa501d568afe4ef9abe5af728522edc352dc088a4351df1b85cfba/rustypot-1.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:936760476f2576968fa0f2411aee16963306e9a284cec98c24138b760ef0110f", size = 1524411, upload-time = "2025-11-19T11:53:16.836Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/c8/fe09616e5632afc1ea261c6ec53ed6e30b1e478112c8891274c3349f523d/rustypot-1.4.0-cp314-cp314-win32.whl", hash = "sha256:36088211b186d438a40ad3013cac3f196e24ec4291904675f4f119738ee0850b", size = 1243384, upload-time = "2025-11-19T11:53:50.65Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/3e/3ef1357731787c2eeb1e9c82f2d87e51daa055e24cedf55079c8c1188932/rustypot-1.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:e0befc88f34336d010bbf2ef75d0245a3537d007dfb2f492f6922dc972246681", size = 1680240, upload-time = "2025-11-19T11:53:49.137Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.15.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" },
+ { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" },
+ { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" },
+ { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" },
+ { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" },
+ { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" },
+ { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" },
+ { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" },
+ { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" },
+ { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" },
+ { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" },
+ { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" },
+ { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" },
+ { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" },
+ { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" },
+ { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" },
+]
+
+[[package]]
+name = "semver"
+version = "3.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730, upload-time = "2025-01-24T13:19:27.617Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload-time = "2025-01-24T13:19:24.949Z" },
+]
+
+[[package]]
+name = "setuptools"
+version = "80.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
+]
+
+[[package]]
+name = "smbus3"
+version = "0.5.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/75/39/4b7fe2b7cfb42a39e5bb4ef2970a3befc0febbf2b5b8ef85fac04ca6dcb3/smbus3-0.5.5.tar.gz", hash = "sha256:91eed38fb6b7d2f893fbfc3f37006aa41e6913fdda5b3a9a79238b353760f92e", size = 22100, upload-time = "2024-06-29T01:55:18.214Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f5/cf/a0cfae35dac3e3d0b1b4242c8ba184616a47550f8c727b0df654d4338e2d/smbus3-0.5.5-py3-none-any.whl", hash = "sha256:7f4cc169975543e67e4b7404168ce918e656f6f8daca5de2bd30527298058083", size = 13616, upload-time = "2024-06-29T01:55:16.434Z" },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
+]
+
+[[package]]
+name = "sounddevice"
+version = "0.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload-time = "2024-10-12T09:40:12.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload-time = "2024-10-12T09:40:05.605Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload-time = "2024-10-12T09:40:07.436Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload-time = "2024-10-12T09:40:09.355Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload-time = "2024-10-12T09:40:11.065Z" },
+]
+
+[[package]]
+name = "soundfile"
+version = "0.13.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e1/41/9b873a8c055582859b239be17902a85339bec6a30ad162f98c9b0288a2cc/soundfile-0.13.1.tar.gz", hash = "sha256:b2c68dab1e30297317080a5b43df57e302584c49e2942defdde0acccc53f0e5b", size = 46156, upload-time = "2025-01-25T09:17:04.831Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/28/e2a36573ccbcf3d57c00626a21fe51989380636e821b341d36ccca0c1c3a/soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445", size = 25751, upload-time = "2025-01-25T09:16:44.235Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/ab/73e97a5b3cc46bba7ff8650a1504348fa1863a6f9d57d7001c6b67c5f20e/soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33", size = 1142250, upload-time = "2025-01-25T09:16:47.583Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/e5/58fd1a8d7b26fc113af244f966ee3aecf03cb9293cb935daaddc1e455e18/soundfile-0.13.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:743f12c12c4054921e15736c6be09ac26b3b3d603aef6fd69f9dde68748f2593", size = 1101406, upload-time = "2025-01-25T09:16:49.662Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ae/c0e4a53d77cf6e9a04179535766b3321b0b9ced5f70522e4caf9329f0046/soundfile-0.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9c9e855f5a4d06ce4213f31918653ab7de0c5a8d8107cd2427e44b42df547deb", size = 1235729, upload-time = "2025-01-25T09:16:53.018Z" },
+ { url = "https://files.pythonhosted.org/packages/57/5e/70bdd9579b35003a489fc850b5047beeda26328053ebadc1fb60f320f7db/soundfile-0.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:03267c4e493315294834a0870f31dbb3b28a95561b80b134f0bd3cf2d5f0e618", size = 1313646, upload-time = "2025-01-25T09:16:54.872Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/df/8c11dc4dfceda14e3003bb81a0d0edcaaf0796dd7b4f826ea3e532146bba/soundfile-0.13.1-py2.py3-none-win32.whl", hash = "sha256:c734564fab7c5ddf8e9be5bf70bab68042cd17e9c214c06e365e20d64f9a69d5", size = 899881, upload-time = "2025-01-25T09:16:56.663Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e9/6b761de83277f2f02ded7e7ea6f07828ec78e4b229b80e4ca55dd205b9dc/soundfile-0.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:1e70a05a0626524a69e9f0f4dd2ec174b4e9567f4d8b6c11d38b5c289be36ee9", size = 1019162, upload-time = "2025-01-25T09:16:59.573Z" },
+]
+
+[[package]]
+name = "stack-data"
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "asttokens" },
+ { name = "executing" },
+ { name = "pure-eval" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
+]
+
+[[package]]
+name = "starlette"
+version = "0.47.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" },
+]
+
+[[package]]
+name = "sympy"
+version = "1.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mpmath" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
+ { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
+ { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
+ { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
+ { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
+ { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
+]
+
+[[package]]
+name = "tornado"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" },
+ { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" },
+ { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
+]
+
+[[package]]
+name = "traitlets"
+version = "5.14.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
+]
+
+[[package]]
+name = "transforms3d"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d5/1e/626c2d87c29a35fadc8de5624f4302e1ee56cff380d282d62cb3780e6620/transforms3d-0.4.2.tar.gz", hash = "sha256:e8b5df30eaedbee556e81c6938e55aab5365894e47d0a17615d7db7fd2393680", size = 1368797, upload-time = "2024-06-17T11:43:33.231Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/7a/f38385f1b2d5f54221baf1db3d6371dc6eef8041d95abff39576c694e9d9/transforms3d-0.4.2-py3-none-any.whl", hash = "sha256:1c70399d9e9473ecc23311fd947f727f7c69ed0b063244828c383aa1aefa5941", size = 1376759, upload-time = "2024-06-20T11:09:19.43Z" },
+]
+
+[[package]]
+name = "types-requests"
+version = "2.32.4.20250913"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
+]
+
+[[package]]
+name = "u-msgpack-python"
+version = "2.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/36/9d/a40411a475e7d4838994b7f6bcc6bfca9acc5b119ce3a7503608c4428b49/u-msgpack-python-2.8.0.tar.gz", hash = "sha256:b801a83d6ed75e6df41e44518b4f2a9c221dc2da4bcd5380e3a0feda520bc61a", size = 18167, upload-time = "2023-05-18T09:28:12.187Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/5e/512aeb40fd819f4660d00f96f5c7371ee36fc8c6b605128c5ee59e0b28c6/u_msgpack_python-2.8.0-py2.py3-none-any.whl", hash = "sha256:1d853d33e78b72c4228a2025b4db28cda81214076e5b0422ed0ae1b1b2bb586a", size = 10590, upload-time = "2023-05-18T09:28:10.323Z" },
+]
+
+[[package]]
+name = "urdf-parser-py"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "lxml" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/8b/9dd44781a4e87746a426c56c3368c3da64fd90a130f582340cbf74397f8e/urdf_parser_py-0.0.4.tar.gz", hash = "sha256:e983f637145fded67bcff6a542302069bb975b2edf1b18318c093abba1b794cc", size = 12952, upload-time = "2021-11-11T17:20:42.012Z" }
+
+[[package]]
+name = "urllib3"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.35.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.21.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f", size = 1442019, upload-time = "2024-10-14T23:37:20.068Z" },
+ { url = "https://files.pythonhosted.org/packages/35/5a/62d5800358a78cc25c8a6c72ef8b10851bdb8cca22e14d9c74167b7f86da/uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d", size = 801898, upload-time = "2024-10-14T23:37:22.663Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/96/63695e0ebd7da6c741ccd4489b5947394435e198a1382349c17b1146bb97/uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26", size = 3827735, upload-time = "2024-10-14T23:37:25.129Z" },
+ { url = "https://files.pythonhosted.org/packages/61/e0/f0f8ec84979068ffae132c58c79af1de9cceeb664076beea86d941af1a30/uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb", size = 3825126, upload-time = "2024-10-14T23:37:27.59Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/fe/5e94a977d058a54a19df95f12f7161ab6e323ad49f4dabc28822eb2df7ea/uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f", size = 3705789, upload-time = "2024-10-14T23:37:29.385Z" },
+ { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523, upload-time = "2024-10-14T23:37:32.048Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8", size = 1447410, upload-time = "2024-10-14T23:37:33.612Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0", size = 805476, upload-time = "2024-10-14T23:37:36.11Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e", size = 3960855, upload-time = "2024-10-14T23:37:37.683Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb", size = 3973185, upload-time = "2024-10-14T23:37:40.226Z" },
+ { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6", size = 3820256, upload-time = "2024-10-14T23:37:42.839Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323, upload-time = "2024-10-14T23:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" },
+ { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" },
+ { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" },
+ { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" },
+ { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" },
+ { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" },
+ { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" },
+ { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" },
+ { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" },
+ { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" },
+ { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" },
+ { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" },
+ { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" },
+ { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" },
+ { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" },
+ { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" },
+ { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" },
+ { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" },
+ { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" },
+ { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" },
+ { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" },
+ { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" },
+ { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" },
+ { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" },
+ { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" },
+ { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" },
+ { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" },
+ { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" },
+ { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" },
+ { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" },
+ { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" },
+ { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" },
+ { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" },
+ { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" },
+ { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" },
+ { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" },
+ { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" },
+ { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" },
+ { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" },
+ { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" },
+ { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" },
+ { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" },
+ { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" },
+ { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" },
+]
+
+[[package]]
+name = "wcwidth"
+version = "0.2.13"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "11.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/3b/2ed38e52eed4cf277f9df5f0463a99199a04d9e29c9e227cfafa57bd3993/websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016", size = 104235, upload-time = "2023-05-07T14:25:20.083Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/76/88640f8aeac7eb0d058b913e7bb72682f8d569db44c7d30e576ec4777ce1/websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac", size = 123714, upload-time = "2023-05-07T14:23:15.309Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/6b/26b28115b46e23e74ede76d95792eedfe8c58b21f4daabfff1e9f159c8fe/websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d", size = 120949, upload-time = "2023-05-07T14:23:17.656Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/82/2d1f3395d47fab65fa8b801e2251b324300ed8db54753b6fb7919cef0c11/websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f", size = 121032, upload-time = "2023-05-07T14:23:20.096Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/ec/56bdd12d847e4fc2d0a7ba2d7f1476f79cda50599d11ffb6080b86f21ef1/websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564", size = 130620, upload-time = "2023-05-07T14:23:21.545Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/fb/ae5ed4be3514287cf8f6c348c87e1392a6e3f4d6eadae75c18847a2f84b6/websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11", size = 129628, upload-time = "2023-05-07T14:23:23.105Z" },
+ { url = "https://files.pythonhosted.org/packages/58/0a/7570e15661a0a546c3a1152d95fe8c05480459bab36247f0acbf41f01a41/websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca", size = 129938, upload-time = "2023-05-07T14:23:24.959Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/6c/5c0322b2875e8395e6bf0eff11f43f3e25da7ef5b12f4d908cd3a19ea841/websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54", size = 134663, upload-time = "2023-05-07T14:23:26.382Z" },
+ { url = "https://files.pythonhosted.org/packages/de/0e/d7274e4d41d7b34f204744c27a23707be2ecefaf6f7df2145655f086ecd7/websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4", size = 133900, upload-time = "2023-05-07T14:23:28.307Z" },
+ { url = "https://files.pythonhosted.org/packages/82/3c/00f051abcf88aec5e952a8840076749b0b26a30c219dcae8ba70200998aa/websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526", size = 134520, upload-time = "2023-05-07T14:23:30.734Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/7b/4d4ecd29be7d08486e38f987a6603c491296d1e33fe55127d79aebb0333e/websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69", size = 124152, upload-time = "2023-05-07T14:23:33.183Z" },
+ { url = "https://files.pythonhosted.org/packages/98/a7/0ed69892981351e5acf88fac0ff4c801fabca2c3bdef9fca4c7d3fde8c53/websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f", size = 124674, upload-time = "2023-05-07T14:23:35.331Z" },
+ { url = "https://files.pythonhosted.org/packages/16/49/ae616bd221efba84a3d78737b417f704af1ffa36f40dcaba5eb954dd4753/websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb", size = 123748, upload-time = "2023-05-07T14:23:37.977Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/84/68b848a373493b58615d6c10e9e8ccbaadfd540f84905421739a807704f8/websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288", size = 120975, upload-time = "2023-05-07T14:23:40.339Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/a8/e81533499f84ef6cdd95d11d5b05fa827c0f097925afd86f16e6a2631d8e/websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d", size = 121017, upload-time = "2023-05-07T14:23:41.874Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ca/65d6986665888494eca4d5435a9741c822022996f0f4200c57ce4b9242f7/websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3", size = 131200, upload-time = "2023-05-07T14:23:43.309Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/a8/a8a582ebeeecc8b5f332997d44c57e241748f8a9856e06a38a5a13b30796/websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b", size = 130195, upload-time = "2023-05-07T14:23:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/5e/b25c60067d700e811dccb4e3c318eeadd3a19d8b3620de9f97434af777a7/websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6", size = 130569, upload-time = "2023-05-07T14:23:46.926Z" },
+ { url = "https://files.pythonhosted.org/packages/14/fc/5cbbf439c925e1e184a0392ec477a30cee2fabc0e63807c1d4b6d570fb52/websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97", size = 136015, upload-time = "2023-05-07T14:23:48.43Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/d8/a997d3546aef9cc995a1126f7d7ade96c0e16c1a0efb9d2d430aee57c925/websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf", size = 135292, upload-time = "2023-05-07T14:23:50.744Z" },
+ { url = "https://files.pythonhosted.org/packages/89/8f/707a05d5725f956c78d252a5fd73b89fa3ac57dd3959381c2d1acb41cb13/websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd", size = 135890, upload-time = "2023-05-07T14:23:52.707Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/94/ac47552208583d5dbcce468430c1eb2ae18962f6b3a694a2b7727cc60d4a/websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c", size = 124149, upload-time = "2023-05-07T14:23:53.848Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/7c/0ad6e7ef0a054d73092f616d20d3d9bd3e1b837554cb20a52d8dd9f5b049/websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8", size = 124670, upload-time = "2023-05-07T14:23:55.812Z" },
+ { url = "https://files.pythonhosted.org/packages/47/96/9d5749106ff57629b54360664ae7eb9afd8302fad1680ead385383e33746/websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6", size = 118056, upload-time = "2023-05-07T14:25:18.508Z" },
+]
+
+[[package]]
+name = "yarl"
+version = "1.20.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" },
+ { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" },
+ { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" },
+ { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" },
+ { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" },
+ { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" },
+ { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" },
+ { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" },
+ { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" },
+ { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" },
+ { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" },
+ { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" },
+ { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" },
+ { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" },
+ { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" },
+ { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" },
+ { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" },
+ { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" },
+ { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" },
+ { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" },
+ { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" },
+ { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" },
+ { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" },
+ { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" },
+ { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" },
+ { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" },
+ { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" },
+ { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" },
+ { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" },
+ { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" },
+ { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" },
+ { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" },
+]
+
+[[package]]
+name = "zipp"
+version = "3.23.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
+]
diff --git a/rmscript/.idea/.gitignore b/rmscript/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4
--- /dev/null
+++ b/rmscript/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/rmscript/.idea/copilot.data.migration.agent.xml b/rmscript/.idea/copilot.data.migration.agent.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4ea72a911af2d782f20b992e808276bb838718d8
--- /dev/null
+++ b/rmscript/.idea/copilot.data.migration.agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/copilot.data.migration.ask.xml b/rmscript/.idea/copilot.data.migration.ask.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7ef04e2ea0752989be4a246d998b53adf46bab42
--- /dev/null
+++ b/rmscript/.idea/copilot.data.migration.ask.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/copilot.data.migration.ask2agent.xml b/rmscript/.idea/copilot.data.migration.ask2agent.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1f2ea11e7f0069b02b6f715f9735b8a31b234538
--- /dev/null
+++ b/rmscript/.idea/copilot.data.migration.ask2agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/copilot.data.migration.edit.xml b/rmscript/.idea/copilot.data.migration.edit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8648f9401aa827dc53e658ac3b1c1f1556df236b
--- /dev/null
+++ b/rmscript/.idea/copilot.data.migration.edit.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/inspectionProfiles/Project_Default.xml b/rmscript/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2969ecaa0b9bfe9d6363eaa2ef4c09f62350419e
--- /dev/null
+++ b/rmscript/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/inspectionProfiles/profiles_settings.xml b/rmscript/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/rmscript/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/misc.xml b/rmscript/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ba42d52e8c1e1e75bc460b697efdcbf2f23a5754
--- /dev/null
+++ b/rmscript/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/modules.xml b/rmscript/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..808fea148f42f29df0f6526c6b4abad3588547df
--- /dev/null
+++ b/rmscript/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/.idea/rmscript.iml b/rmscript/.idea/rmscript.iml
new file mode 100644
index 0000000000000000000000000000000000000000..749e725db0752f36e5c13f06bb47f8057632568c
--- /dev/null
+++ b/rmscript/.idea/rmscript.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rmscript/README.md b/rmscript/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ca8752f2d2fdb896a190f7fbd0c89133ab357fcf
--- /dev/null
+++ b/rmscript/README.md
@@ -0,0 +1,86 @@
+# rmscript
+
+A kid-friendly robot programming language for Reachy Mini.
+
+## Overview
+
+rmscript is a simple, Python-like domain-specific language (DSL) designed to make robot programming accessible to children and beginners. It compiles to an intermediate representation (IR) that can be executed by different adapters (queue-based execution, WebSocket streaming, etc.).
+
+## Installation
+
+```bash
+pip install rmscript
+```
+
+With optional dependencies:
+```bash
+pip install rmscript[reachy] # For reachy_mini integration
+pip install rmscript[scipy] # For advanced transformations
+```
+
+## Quick Start
+
+```python
+from rmscript import compile_script
+
+# Compile rmscript source
+result = compile_script("""
+DESCRIPTION Wave hello
+look left
+antenna both up
+wait 1s
+look right
+""")
+
+if result.success:
+ print(f"Compiled {len(result.ir)} actions")
+ for action in result.ir:
+ print(f" - {type(action).__name__}")
+else:
+ for error in result.errors:
+ print(f"Error: {error}")
+```
+
+## Language Features
+
+- **Intuitive syntax**: `look left`, `turn right`, `antenna up`
+- **Time control**: `wait 2s`, `slow`, `fast`
+- **Qualitative strengths**: `little`, `medium`, `lot`, `maximum`
+- **Compound movements**: `look left and up`
+- **Loops**: `repeat 3` blocks
+- **Sound & pictures**: `play sound`, `picture`
+
+See the [Language Reference](docs/language_reference.md) for complete syntax.
+
+## Architecture
+
+rmscript uses a 5-stage compilation pipeline:
+
+```
+Source → Lexer → Parser → Semantic Analyzer → Optimizer → IR
+```
+
+The compiler outputs **Intermediate Representation (IR)**, not executable code. Execution is handled by **adapters** that consume IR in different contexts.
+
+## For Developers
+
+### Adapters
+
+Adapters execute IR in specific contexts. Example:
+
+```python
+from rmscript import compile_file, ExecutionAdapter, ExecutionContext
+from dataclasses import dataclass
+
+@dataclass
+class MyContext(ExecutionContext):
+ robot: Any
+
+class MyAdapter:
+ def execute(self, ir, context: MyContext) -> dict:
+ for action in ir:
+ # Execute each IR action
+ if isinstance(action, IRAction):
+ context.robot.move(action.head_pose)
+ return {"status": "complete"}
+```
\ No newline at end of file
diff --git a/rmscript/pyproject.toml b/rmscript/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..dec328b2abba9cc9ee5fa3cd61a9aa7293c8ea55
--- /dev/null
+++ b/rmscript/pyproject.toml
@@ -0,0 +1,37 @@
+[project]
+name = "rmscript"
+version = "0.1.0"
+description = "A kid-friendly robot programming language for Reachy Mini"
+readme = "README.md"
+requires-python = ">=3.11"
+
+dependencies = [
+ "numpy>=1.24.0",
+ "reachy_mini>=1.0.0",
+]
+
+[project.optional-dependencies]
+dev = [
+ "pytest>=7.0.0",
+ "pytest-asyncio>=0.21.0",
+ "ruff>=0.1.0",
+ "scipy>=1.10.0", # For testing rotation matrices
+]
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.ruff]
+line-length = 100
+target-version = "py311"
+
+[tool.ruff.lint]
+select = ["E", "F", "I", "N", "W"]
+ignore = []
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+python_files = ["test_*.py"]
+python_classes = ["Test*"]
+python_functions = ["test_*"]
diff --git a/rmscript/rmscript_reference_doc.md b/rmscript/rmscript_reference_doc.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7ea53879237df00164e841450a26a494b0c7d7a
--- /dev/null
+++ b/rmscript/rmscript_reference_doc.md
@@ -0,0 +1,1021 @@
+# rmscript - Kid-Friendly Robot Programming
+
+rmscript is a natural language-inspired programming language designed to make robot programming accessible and fun for children. It compiles to Python code that controls the Reachy Mini robot.
+
+## Example Usage
+
+Create a file `hello.rmscript`:
+
+```rmscript
+DESCRIPTION Wave hello to someone
+antenna up
+wait 1s
+antenna down
+look left
+look right
+look center
+```
+
+Test the script using the provided runner (after starting the reachy-mini-daemon):
+
+```
+python src/reachy_mini_conversation_app/rmscript/run_rmscript.py path/to/hello.rmscript
+```
+
+
+## Language Syntax
+
+### File Structure
+
+Every rmscript file has a simple structure:
+
+```rmscript
+DESCRIPTION Wave hello to someone
+# Your commands here
+look left
+wait 1s
+```
+
+**Key features:**
+- **Tool name**: Automatically derived from the filename (e.g., `wave_hello.rmscript` → tool name is `wave_hello`)
+- **DESCRIPTION** (optional): One-line description used for LLM tool registration
+
+### Basic Commands
+
+```rmscript
+# Comments start with #
+
+# Movement commands
+look left
+turn right
+antenna up
+head forward 10
+
+# Wait command
+wait 2s
+wait 0.5s
+
+# Camera command
+picture
+
+# Sound playback
+play mysound
+play othersound pause
+```
+
+### Case Insensitivity
+
+rmscript is case-insensitive for keywords:
+
+```rmscript
+LOOK left # Same as "look left"
+Look Left # Same as "look left"
+WAIT 1s # Same as "wait 1s"
+```
+
+## Movement Commands
+
+### Look (Head Orientation)
+
+Control the robot's head orientation (pitch and yaw):
+
+```rmscript
+look left # Turn head left (30° default)
+look right 45 # Turn head right 45°
+look up # Tilt head up (30° default)
+look down 20 # Tilt head down 20°
+look center # Return to center position
+
+# Synonyms
+look straight # Same as center
+look neutral # Same as center
+```
+
+**Physical Limits:**
+- Pitch (up/down): ±40°
+- Yaw (left/right): ±180° absolute, ±65° relative to body
+
+### Turn (Body Rotation)
+
+Rotate the robot's body (the head rotates together with the body):
+
+```rmscript
+turn left # Rotate body and head left (30° default)
+turn right 90 # Rotate body and head right 90°
+turn center # Face forward
+```
+
+**Note:** The `turn` command rotates both the body yaw and the head yaw together, since the body carries the head.
+
+**Physical Limits:**
+- Body yaw: ±160° (safe range)
+
+### Antenna
+
+Control the antenna positions using multiple syntaxes:
+
+**Clock Position (Numeric 0-12):**
+```rmscript
+antenna both 0 # 0 o'clock = 0° (straight up)
+antenna both 3 # 3 o'clock = 90° (external/right)
+antenna both 6 # 6 o'clock = 180° (straight down)
+antenna both 9 # 9 o'clock = -90° (internal/left)
+antenna left 4.5 # Left antenna to 4.5 o'clock (135°)
+```
+
+**Clock Keywords:**
+```rmscript
+antenna both high # 0° (high position)
+antenna both ext # 90° (external)
+antenna both low # 180° (low position)
+antenna both int # -90° (internal)
+```
+
+**Directional Keywords (Natural Language):**
+```rmscript
+antenna both up # 0° (up)
+antenna both right # 90° (right/external)
+antenna both down # 180° (down)
+antenna both left # -90° (left/internal)
+
+# Individual antenna control
+antenna left up # Left antenna pointing up
+antenna right down # Right antenna pointing down
+
+# Even in potentially confusing cases, it works naturally:
+antenna left left # Left antenna pointing left (-90°)
+antenna right right # Right antenna pointing right (90°)
+```
+
+**Physical Limits:**
+- Antenna angle: ±180° (safe recommended: ±120°)
+
+### Head Translation
+
+Move the head forward/back/left/right/up/down in space:
+
+```rmscript
+head forward 10 # Move head forward 10mm
+head back 5 # Move head back 5mm
+head backward 5 # Same as "back" (synonym)
+head backwards 5 # Same as "back" (synonym)
+head left 8 # Move head left 8mm
+head right 8 # Move head right 8mm
+head up 5 # Move head up 5mm
+head down 3 # Move head down 3mm
+```
+
+**Physical Limits:**
+- X (forward/back): ±50mm typical
+- Y (left/right): ±50mm typical
+- Z (up/down): +20mm / -30mm typical
+
+### Tilt (Head Roll)
+
+Tilt the head side-to-side:
+
+```rmscript
+tilt left 15 # Tilt head left
+tilt right 15 # Tilt head right
+tilt center # Return to level
+```
+
+**Physical Limits:**
+- Roll: ±40°
+
+### Wait
+
+Pause between movements:
+
+```rmscript
+wait 1s # Wait 1 second
+wait 0.5s # Wait 0.5 seconds
+wait 2.5s # Wait 2.5 seconds
+```
+
+**Important:** The `s` suffix is **required** for consistency. `wait 1` will produce a compilation error.
+
+### Picture
+
+Take a picture with the robot's camera and add it to the conversation:
+
+```rmscript
+picture # Take a picture
+```
+
+The picture command captures a frame from the camera and returns it as a base64-encoded image.
+
+**How it works:**
+
+1. **During execution**: The `picture` command is queued as a movement (instant duration: 0.01s)
+2. **Capture timing**: Picture is captured at the right moment in the movement sequence
+3. **File storage**: Pictures are automatically saved to `/tmp/reachy_picture_YYYYMMDD_HHMMSS_microseconds.jpg`
+4. **Color handling**: Images are captured in RGB format with proper color conversion
+5. **LLM integration**: When called by the LLM, pictures are automatically added to the conversation
+
+**Single picture (LLM-compatible):**
+```rmscript
+DESCRIPTION Check behind by taking a picture
+turn left maximum
+wait 0.5s
+picture
+wait 0.5s
+turn center
+```
+
+This script:
+- Turns the robot to look behind (120° left)
+- Waits for movement to stabilize
+- Captures a picture at the right moment
+- Returns to center position
+- The LLM receives the picture and can describe what it sees
+
+**Important timing notes:**
+- Add `wait` commands before `picture` to let movements complete
+- The tool automatically waits for all movements + picture capture before returning
+- Pictures are captured when the movement queue reaches them (not immediately)
+
+**Multiple pictures:**
+```rmscript
+DESCRIPTION Look around and take pictures
+look left
+wait 0.5s
+picture
+look right
+wait 0.5s
+picture
+look center
+```
+
+Each `picture` command captures a separate image. For single-picture scripts, the image is returned in Camera-compatible format (`b64_im`) for LLM integration. For multi-picture scripts, images are returned as an array.
+
+**Use with `run_rmscript.py`:**
+
+When testing scripts with `run_rmscript.py`, pictures are:
+- Saved to `/tmp` with timestamped filenames
+- Logged with full paths for easy access
+- Displayed in the execution summary
+
+**Use with LLM conversation:**
+
+When called as a tool by the LLM:
+- Single picture: Automatically fed to the LLM conversation (like Camera tool)
+- Multiple pictures: Available as base64 data (future enhancement for multi-image display)
+- Gradio UI: Picture thumbnails displayed in the conversation interface
+
+**Example conversation:**
+```
+User: "Can you check what's behind you?"
+→ LLM calls: check_behind()
+→ Robot: turns, captures picture, returns to center
+→ LLM sees the picture and responds: "I can see a desk with a laptop and some books!"
+```
+
+### Play Sound
+
+Play sound files (.wav, .mp3, .ogg, .flac) during script execution:
+
+```rmscript
+play soundname # Play sound in background (async, continues immediately)
+play soundname pause # Wait for sound to finish before continuing (blocking)
+play soundname 5s # Play sound for exactly 5 seconds (blocking)
+```
+
+The script automatically searches for sound files in this order:
+1. **Directory containing the .rmscript file** (highest priority - co-located sounds)
+2. Current working directory
+3. `sounds/` subdirectory
+
+**Asynchronous playback (default):**
+```rmscript
+play intro # Starts playing, script continues immediately
+look left
+wait 1s
+```
+The sound plays in the background while movements execute.
+
+**Blocking playback (wait for sound to finish):**
+```rmscript
+play mysound pause # Waits for sound to finish
+# OR use synonyms:
+play mysound fully
+play mysound wait
+play mysound block
+play mysound complete
+```
+
+The script pauses until the sound finishes playing, then continues.
+
+**Duration-limited playback:**
+```rmscript
+play mysound 10s # Play sound for exactly 10 seconds (blocks for 10s)
+```
+
+Plays the sound for the specified duration. If the sound is shorter, it will finish early. If longer, it will be cut off.
+
+### Loop Sound
+
+Loop a sound file repeatedly for a specified duration:
+
+```rmscript
+loop soundname # Loop sound for 10 seconds (default)
+loop soundname 5s # Loop sound for 5 seconds
+loop soundname 30s # Loop sound for 30 seconds
+```
+
+The loop command automatically repeats the sound until the duration expires. This is useful for background music or ambient sounds.
+
+**Example use case:**
+```rmscript
+DESCRIPTION Greet with sound and movement
+play hello pause # Play greeting sound fully
+wait 0.5s
+antenna both up # Wave antennas
+wait 1s
+antenna both down
+```
+
+**Sound files:**
+- File extensions: `.wav`, `.mp3`, `.ogg`, `.flac`
+- Example: `play hello` looks for `hello.wav`, `hello.mp3`, etc.
+- **Best practice**: Place sound files in the same directory as your .rmscript file
+- Alternative locations: working directory or `sounds/` subdirectory
+
+## Advanced Features
+
+### Keyword Reuse with `and`
+
+Chain multiple directions with the same action keyword:
+
+```rmscript
+# Reuse "look" keyword
+look left and up 25
+# → Equivalent to: look left + look up 25
+# → Merged into single command
+
+# Different keywords - no reuse
+turn left and look right
+# → turn left + look right
+# → Two separate commands merged
+```
+
+This creates more natural, flowing descriptions while optimizing execution.
+
+### Qualitative Strength
+
+Use descriptive words instead of numbers - works for both angles and distances:
+
+```rmscript
+# 5 levels of strength - values are context-aware!
+
+# Examples:
+turn left tiny # 10° (body yaw can handle larger movements)
+look up tiny # 5° (head pitch limited by cone constraint)
+head forward tiny # 2mm
+
+turn left maximum # 120° (body yaw safe maximum)
+look up maximum # 38° (respects ±40° pitch limit)
+look left maximum # 60° (respects ±65° body-head differential)
+head forward maximum # 28mm (under 30mm limit)
+```
+
+**Available Qualitative Keywords:**
+- **VERY_SMALL**: minuscule, mini, verysmall, tiny
+- **SMALL**: little, slightly, small, alittle
+- **MEDIUM**: medium, normal, regular, standard, normally
+- **LARGE**: lot, big, large, very, alot, huge, strong, strongly
+- **VERY_LARGE**: verybig, enormous, verylarge, maximum
+
+**Context-Aware Values:**
+
+The same qualitative keyword maps to different values depending on the movement type, respecting physical limits:
+
+| Keyword | VERY_SMALL | SMALL | MEDIUM | LARGE | VERY_LARGE |
+|---------|------------|-------|--------|-------|------------|
+| **Body Yaw (turn)** | 10° | 30° | 60° | 90° | 120° |
+| **Head Pitch/Roll (look up/down, tilt)** | 5° | 10° | 20° | 30° | 38° |
+| **Head Yaw (look left/right)** | 5° | 15° | 30° | 45° | 60° |
+| **Head Translation (head forward/back/etc)** | 2mm | 5mm | 10mm | 20mm | 28mm |
+| **Antennas** | 10° | 30° | 60° | 90° | 110° |
+
+**Note:** These values are carefully chosen to stay within the robot's physical limits while maximizing safe range of motion.
+
+### Duration Keywords
+
+Use descriptive speed words:
+
+```rmscript
+look left superfast # 0.2 seconds
+look right fast # 0.5 seconds
+look up slow # 2.0 seconds
+look up slowly # 2.0 seconds (synonym for slow)
+look down superslow # 3.0 seconds
+```
+
+Combine with 'and':
+
+```rmscript
+turn left and look right fast
+# Both movements complete in 0.5 seconds
+```
+
+### Repeat Blocks
+
+Repeat a sequence of commands:
+
+```rmscript
+repeat 3
+ look left
+ wait 0.5s
+ look right
+ wait 0.5s
+
+# Nested repeat blocks work too
+repeat 2
+ antenna up
+ repeat 3
+ look left
+ look right
+ antenna down
+```
+
+**Indentation:** Use spaces or tabs consistently (tabs = 4 spaces).
+
+### Combining Movements
+
+Combine multiple movements into a single smooth motion:
+
+```rmscript
+# All happen simultaneously
+antenna both up and look up 25 and turn left 30
+```
+
+This merges into a single `goto_target()` call with all parameters.
+
+**Important:** The `and` keyword can only combine movement commands (turn, look, head, tilt, antenna). You **cannot** combine movements with control commands (picture, play, loop, wait) using `and`. Use separate lines instead:
+
+```rmscript
+# ❌ ERROR - Cannot combine movement with picture
+look left and picture
+
+# ✓ CORRECT - Use separate lines
+look left
+wait 0.5s
+picture
+
+# ❌ ERROR - Cannot combine movement with play
+turn right and play mysound
+
+# ✓ CORRECT - Use separate lines
+turn right
+play mysound
+```
+
+## Compiler Architecture
+
+### Overview
+
+The compiler uses a 5-stage pipeline:
+
+```
+Source Code → Lexer → Parser → Semantic Analyzer → Optimizer → Code Generator
+```
+
+### Stage 1: Lexer (Tokenization)
+
+Converts source text into tokens with indentation tracking:
+
+```python
+from reachy_mini.rmscript.lexer import Lexer
+
+source = "look left\nwait 1s"
+lexer = Lexer(source)
+tokens = lexer.tokenize()
+# → [Token(KEYWORD_LOOK, 'look', L1:C1), Token(DIRECTION, 'left', L1:C6), ...]
+```
+
+Features:
+- Python-like INDENT/DEDENT tokens
+- Case-insensitive keyword matching
+- Comment stripping
+- Line/column tracking for error messages
+
+### Stage 2: Parser (AST Construction)
+
+Builds an Abstract Syntax Tree from tokens:
+
+```python
+from reachy_mini.rmscript.parser import Parser
+
+ast = Parser(tokens).parse()
+# → Program(tool_name='...', description='...', statements=[...])
+```
+
+Features:
+- Keyword reuse detection for `and` chains
+- Direction validation per keyword
+- Repeat block nesting
+- Syntax error reporting
+
+### Stage 3: Semantic Analyzer
+
+Applies defaults, validates ranges, and generates IR:
+
+```python
+from reachy_mini.rmscript.semantic import SemanticAnalyzer
+
+ir, errors, warnings = SemanticAnalyzer().analyze(ast)
+# → List[Action | WaitAction], List[CompilationError], List[CompilationError]
+```
+
+Features:
+- Default value resolution (30° for angles, 1s for duration)
+- Qualitative → quantitative conversion
+- Duration keyword mapping
+- Physical limit validation
+- Action merging (multiple SingleActions → one Action)
+
+### Stage 4: Optimizer
+
+Optimizes the IR:
+
+```python
+from reachy_mini.rmscript.optimizer import Optimizer
+
+optimized_ir = Optimizer().optimize(ir)
+```
+
+Features:
+- Consecutive wait merging
+- No-op action removal
+
+### Stage 5: Code Generator
+
+Creates executable Python functions:
+
+```python
+from reachy_mini.rmscript.codegen import CodeGenerator
+
+executable = CodeGenerator().generate(tool_name, description, ir)
+# → Callable that executes the behavior
+```
+
+Features:
+- Direct execution: `executable(mini)`
+- Readable code generation: `to_python_code()`
+- Proper imports and function signatures
+
+## API Reference
+
+### rmscriptCompiler
+
+Main compiler class.
+
+```python
+from reachy_mini.rmscript import rmscriptCompiler
+
+compiler = rmscriptCompiler(log_level="INFO")
+tool = compiler.compile(source_code)
+```
+
+**Parameters:**
+- `log_level` (str): Logging level - "DEBUG", "INFO", "WARNING", "ERROR"
+
+**Returns:**
+- `CompiledTool`: Compilation result object
+
+### verify_rmscript
+
+Verification function that checks if rmscript code compiles without generating executable code.
+
+```python
+from reachy_mini_conversation_app.rmscript import verify_rmscript
+
+is_valid, errors = verify_rmscript(source_code)
+if not is_valid:
+ for error in errors:
+ print(error)
+```
+
+**Parameters:**
+- `source_code` (str): rmscript source code to verify
+
+**Returns:**
+- `tuple[bool, list[str]]`: Tuple of (is_valid, error_messages)
+ - `is_valid`: True if compilation succeeds, False otherwise
+ - `error_messages`: List of error and warning messages (empty if valid)
+
+**Example:**
+
+```python
+script = """
+DESCRIPTION Test script
+look left and picture
+"""
+
+is_valid, errors = verify_rmscript(script)
+# is_valid = False
+# errors = ["❌ Line 3: Cannot combine movement with 'picture' using 'and'. Use separate lines instead."]
+```
+
+### CompiledTool
+
+Result of compilation.
+
+```python
+class CompiledTool:
+ name: str # Tool name
+ description: str # Tool description
+ executable: Callable # Function to execute on robot
+ success: bool # Compilation succeeded?
+ errors: List[CompilationError] # Compilation errors
+ warnings: List[CompilationError] # Compilation warnings
+ source_code: str # Original source
+ ir: List[Action | WaitAction] # Intermediate representation
+
+ def execute(self, mini: ReachyMini) -> None:
+ """Execute the compiled behavior on the robot."""
+
+ def to_python_code(self) -> str:
+ """Generate readable Python code."""
+```
+
+**Usage:**
+
+```python
+if tool.success:
+ # Execute on robot
+ with ReachyMini() as mini:
+ tool.execute(mini)
+
+ # Or get Python code
+ print(tool.to_python_code())
+else:
+ # Handle errors
+ for error in tool.errors:
+ print(f"{error.severity} Line {error.line}: {error.message}")
+```
+
+### CompilationError
+
+Error or warning from compilation.
+
+```python
+@dataclass
+class CompilationError:
+ line: int # Line number (1-indexed)
+ column: int # Column number (1-indexed)
+ message: str # Error/warning message
+ severity: str # "ERROR" or "WARNING"
+```
+
+### Action (IR)
+
+Intermediate representation of a movement.
+
+```python
+@dataclass
+class Action:
+ head_pose: Optional[np.ndarray] # 4x4 transformation matrix
+ antennas: Optional[np.ndarray] # [left, right] angles in radians
+ body_yaw: Optional[float] # Body rotation in radians
+ duration: float # Movement duration in seconds
+ interpolation: str # "minjerk", "linear", "ease", "cartoon"
+```
+
+### WaitAction (IR)
+
+Intermediate representation of a wait/pause.
+
+```python
+@dataclass
+class WaitAction:
+ duration: float # Wait duration in seconds
+```
+
+## Examples
+
+### Example 1: Simple Greeting
+
+```rmscript
+
+DESCRIPTION Greet someone warmly
+# Wave with antennas
+antenna up
+wait 0.5s
+antenna down
+wait 0.5s
+
+# Nod
+look down 20
+wait 0.3s
+look up 20
+wait 0.3s
+look center
+```
+
+### Example 2: Search Pattern
+
+```rmscript
+
+DESCRIPTION Look around to search for something
+# Scan left to right
+look left 45
+wait 1s
+look center
+wait 0.5s
+look right 45
+wait 1s
+look center
+
+# Check up and down
+look up 30
+wait 0.5s
+look down 30
+wait 0.5s
+look center
+```
+
+### Example 3: Dance Choreography
+
+```rmscript
+
+DESCRIPTION Perform a fun dance routine
+# Opening pose
+antenna both up and look up 25
+wait 1s
+
+# Main sequence
+repeat 2
+ # Move left
+ turn left 30 and look right 20
+ wait 0.5s
+
+ # Move right
+ turn right 30 and look left 20
+ wait 0.5s
+
+# Ending pose
+turn center and look center and antenna down
+```
+
+### Example 4: Using Qualitative Strength
+
+```rmscript
+
+DESCRIPTION Wave shyly at someone
+# Small movements
+look down slightly
+antenna left up a little
+wait 1s
+antenna left down
+look center
+```
+
+### Example 5: Complex Combination
+
+```rmscript
+
+DESCRIPTION Show excitement when greeting
+# Quick movements
+repeat 3
+ antenna both up superfast
+ antenna both down superfast
+
+# Energetic looking around
+look left and up fast
+wait 0.2s
+look right and up fast
+wait 0.2s
+look center slow
+```
+
+## Error Handling
+
+### Compilation Errors
+
+Errors prevent code generation:
+
+```rmscript
+# Invalid keyword
+jump up
+```
+**Output:** `❌ Line 1: Unexpected token: 'jump'`
+
+```rmscript
+# Invalid direction for keyword
+turn up
+```
+**Output:** `❌ Line 1: Invalid direction 'up' for keyword 'turn'`
+
+```rmscript
+# Missing indentation
+repeat 3
+look left
+```
+**Output:** `❌ Line 2: Expected indented block after 'repeat'`
+
+### Compilation Warnings
+
+Warnings allow compilation but alert to potential issues:
+
+```rmscript
+# Exceeds safe range
+turn left 200
+```
+**Output:** `⚠️ Line 1: Body yaw 200.0° exceeds safe range (±160.0°), will be clamped`
+
+```rmscript
+# Exceeds physical limit
+look up 50
+```
+**Output:** `⚠️ Line 1: Head pitch 50.0° exceeds limit (±40.0°), will be clamped`
+
+### Handling Errors in Code
+
+```python
+tool = compiler.compile(source)
+
+if not tool.success:
+ print("Compilation failed!")
+ for error in tool.errors:
+ print(f" ❌ Line {error.line}: {error.message}")
+ exit(1)
+
+if tool.warnings:
+ print("Compilation succeeded with warnings:")
+ for warning in tool.warnings:
+ print(f" ⚠️ Line {warning.line}: {warning.message}")
+
+# Safe to execute
+tool.execute(mini)
+```
+
+## Tips and Best Practices
+
+### 1. Use Comments
+
+```rmscript
+# Good: explain what you're doing
+# Opening sequence: get attention
+antenna up
+wait 1s
+
+# Main behavior: scan the room
+look left 45
+wait 1s
+look right 45
+```
+
+### 2. Use Descriptive Tool Names
+
+```python
+# Good
+
+
+
+
+# Avoid
+
+
+
+```
+
+### 3. Break Complex Behaviors into Steps
+
+```rmscript
+# Good: readable steps
+antenna up
+wait 1s
+look left
+wait 0.5s
+look right
+wait 0.5s
+look center
+
+# Avoid: too compressed
+antenna up and look left and wait 1s and look right
+```
+
+### 4. Use Repeat for Patterns
+
+```rmscript
+# Good: use repeat
+repeat 3
+ antenna up
+ antenna down
+
+# Avoid: repetitive
+antenna up
+antenna down
+antenna up
+antenna down
+antenna up
+antenna down
+```
+
+### 5. Test with Small Values First
+
+```rmscript
+# Start conservative
+look left 10
+turn right 15
+
+# Then increase if needed
+look left 45
+turn right 90
+```
+
+## Physical Safety Limits
+
+rmscript validates all movements against these limits:
+
+| Movement | Limit | Warning Threshold |
+|----------|-------|------------------|
+| Body yaw (turn) | ±180° | ±160° |
+| Head yaw (look left/right) | ±180° absolute | ±65° relative to body |
+| Head pitch (look up/down) | ±40° | ±40° |
+| Head roll (tilt) | ±40° | ±40° |
+| Antenna | ±90° | ±65° |
+| Head X (forward/back) | ±50mm typical | - |
+| Head Y (left/right) | ±50mm typical | - |
+| Head Z (up/down) | +20mm / -30mm typical | - |
+
+Exceeding these limits generates **warnings** but still compiles. The robot's hardware will clamp values to safe ranges.
+
+## Default Values
+
+When values aren't specified:
+
+| Parameter | Default |
+|-----------|---------|
+| Angle (look, turn, tilt) | 30° |
+| Distance (head translation) | 10mm |
+| Antenna angle | 45° |
+| Duration | 1.0s |
+
+**Qualitative Strength Values:**
+
+| Level | Keywords | Angle | Distance |
+|-------|----------|-------|----------|
+| VERY_SMALL | tiny, minuscule, mini, verysmall | 5° | 2mm |
+| SMALL | little, slightly, small, alittle | 15° | 5mm |
+| MEDIUM | medium, normal, regular, standard, normally | 30° | 10mm |
+| LARGE | strong, lot, big, large, very, alot, huge, strongly | 45° | 20mm |
+| VERY_LARGE | enormous, verybig, verylarge, maximum | 60° | 30mm |
+
+## Troubleshooting
+
+### Issue: "Inconsistent indentation"
+
+**Cause:** Mixing tabs and spaces, or inconsistent indentation levels.
+
+**Solution:** Use only spaces or only tabs (not mixed). Ensure nested blocks are indented consistently.
+
+```rmscript
+# Bad: mixed tabs and spaces
+repeat 3
+ look left # 4 spaces
+→ look right # 1 tab (shows as →)
+
+# Good: consistent spaces
+repeat 3
+ look left # 4 spaces
+ look right # 4 spaces
+```
+
+### Issue: "Expected indented block after 'repeat'"
+
+**Cause:** No indentation after `repeat`.
+
+**Solution:** Indent the repeat block body.
+
+```rmscript
+# Bad
+repeat 3
+look left
+
+# Good
+repeat 3
+ look left
+```
+
+### Issue: Generated code has unexpected angles
+
+**Cause:** Sign convention confusion (left/right).
+
+**Solution:** Remember: `left` = positive yaw, `right` = negative yaw in the generated code. `up` = negative pitch, `down` = positive pitch. This matches the robot's actual coordinate system.
+
+
+## Contributing
+
+To extend rmscript:
+
+1. **Add new keywords**: Update `lexer.py`, `parser.py`, and `semantic.py`
+2. **Add new features**: Modify the appropriate compiler stage
+3. **Add tests**: Create integration tests in `tests/test_rmscript/`
+4. **Update docs**: Keep this README and examples current
+
+## License
+
+Same as Reachy Mini SDK.
diff --git a/rmscript/src/rmscript/__init__.py b/rmscript/src/rmscript/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..87a5181531eea4bc3338cb06ff7c329b7c054409
--- /dev/null
+++ b/rmscript/src/rmscript/__init__.py
@@ -0,0 +1,32 @@
+"""rmscript: A kid-friendly robot programming language for Reachy Mini."""
+
+from rmscript.compiler import RMScriptCompiler, compile_script, compile_file, verify_script
+from rmscript.ir import (
+ CompilationResult,
+ CompilationError,
+ IRAction,
+ IRWaitAction,
+ IRPictureAction,
+ IRPlaySoundAction,
+)
+from rmscript.adapters import ExecutionAdapter, ExecutionContext
+
+__version__ = "0.1.0"
+
+__all__ = [
+ # Compiler
+ "RMScriptCompiler",
+ "compile_script",
+ "compile_file",
+ "verify_script",
+ # IR types
+ "CompilationResult",
+ "CompilationError",
+ "IRAction",
+ "IRWaitAction",
+ "IRPictureAction",
+ "IRPlaySoundAction",
+ # Adapter protocol
+ "ExecutionAdapter",
+ "ExecutionContext",
+]
diff --git a/rmscript/src/rmscript/adapters/__init__.py b/rmscript/src/rmscript/adapters/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cd2e4aace6a85914305192ec8ecb721212e6072
--- /dev/null
+++ b/rmscript/src/rmscript/adapters/__init__.py
@@ -0,0 +1,5 @@
+"""Execution adapters for rmscript."""
+
+from rmscript.adapters.base import ExecutionAdapter, ExecutionContext
+
+__all__ = ["ExecutionAdapter", "ExecutionContext"]
diff --git a/rmscript/src/rmscript/adapters/base.py b/rmscript/src/rmscript/adapters/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..689a98238994e664af135daf5629ec7973ec7d8c
--- /dev/null
+++ b/rmscript/src/rmscript/adapters/base.py
@@ -0,0 +1,42 @@
+"""Base adapter protocol for executing rmscript IR."""
+
+from typing import Protocol, Any, Dict, List, Optional
+from dataclasses import dataclass
+
+from rmscript.ir import IRAction, IRWaitAction, IRPictureAction, IRPlaySoundAction
+
+
+@dataclass
+class ExecutionContext:
+ """Base context for all adapters."""
+
+ script_name: str
+ script_description: str
+ source_file_path: Optional[str] = None
+
+
+class ExecutionAdapter(Protocol):
+ """Protocol for execution adapters.
+
+ Adapters consume IR and execute it in adapter-specific ways.
+ Examples:
+ - QueueExecutionAdapter: Converts IR to movement queue for robot execution
+ - WebSocketExecutionAdapter: Streams pose parameters over WebSocket
+ - SimulationAdapter: Runs IR in a simulation environment
+ """
+
+ def execute(
+ self,
+ ir: List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction],
+ context: ExecutionContext,
+ ) -> Dict[str, Any]:
+ """Execute IR in adapter-specific way.
+
+ Args:
+ ir: List of IR actions to execute
+ context: Execution context with metadata
+
+ Returns:
+ Dictionary with execution results (adapter-specific format)
+ """
+ ...
diff --git a/rmscript/src/rmscript/ast_nodes.py b/rmscript/src/rmscript/ast_nodes.py
new file mode 100644
index 0000000000000000000000000000000000000000..3263d6196915ae351480e91dbd36d653eed92a6d
--- /dev/null
+++ b/rmscript/src/rmscript/ast_nodes.py
@@ -0,0 +1,96 @@
+"""Abstract Syntax Tree node definitions for ReachyMiniScript."""
+
+from typing import List, Optional
+from dataclasses import dataclass
+
+
+@dataclass
+class ASTNode:
+ """Base class for all AST nodes."""
+
+ line: int = 0
+ column: int = 0
+
+
+@dataclass
+class Program(ASTNode):
+ """Root node of the AST."""
+
+ tool_name: str = ""
+ description: str = ""
+ statements: List["Statement"] = None # type: ignore
+
+ def __post_init__(self) -> None:
+ """Initialize mutable defaults."""
+ if self.statements is None:
+ self.statements = []
+
+
+@dataclass
+class Statement(ASTNode):
+ """Base class for statements."""
+
+ pass
+
+
+@dataclass
+class ActionChain(Statement):
+ """Chain of actions connected by 'and' keyword."""
+
+ actions: List["SingleAction"] = None # type: ignore
+
+ def __post_init__(self) -> None:
+ """Initialize mutable defaults."""
+ if self.actions is None:
+ self.actions = []
+
+
+@dataclass
+class SingleAction(ASTNode):
+ """Single action (may be part of a chain)."""
+
+ keyword: str = "" # turn, look, head, tilt, antenna
+ direction: Optional[str] = None # left, right, up, down, etc.
+ antenna_modifier: Optional[str] = None # left, right, both (for antenna only)
+ strength: Optional[float] = None # Numerical value (degrees or mm)
+ strength_qualitative: Optional[str] = None # little, lot, etc.
+ duration: Optional[float] = None # seconds
+ duration_keyword: Optional[str] = None # fast, slow, etc.
+
+
+@dataclass
+class RepeatBlock(Statement):
+ """Repeat block with nested statements."""
+
+ count: int = 0
+ body: List[Statement] = None # type: ignore
+
+ def __post_init__(self) -> None:
+ """Initialize mutable defaults."""
+ if self.body is None:
+ self.body = []
+
+
+@dataclass
+class WaitStatement(Statement):
+ """Wait/pause statement."""
+
+ duration: Optional[float] = None
+ duration_keyword: Optional[str] = None
+
+
+@dataclass
+class PictureStatement(Statement):
+ """Take a picture statement."""
+
+ pass
+
+
+@dataclass
+class PlaySoundStatement(Statement):
+ """Play a sound statement."""
+
+ sound_name: str = "" # Name of the sound file (without extension)
+ blocking: bool = False # True = wait for sound to finish
+ loop: bool = False # True = loop the sound
+ duration: Optional[float] = None # Duration for looping (or limited playback)
diff --git a/rmscript/src/rmscript/compiler.py b/rmscript/src/rmscript/compiler.py
new file mode 100644
index 0000000000000000000000000000000000000000..8308294a862467fb090538f1f89fa490d6cb32f9
--- /dev/null
+++ b/rmscript/src/rmscript/compiler.py
@@ -0,0 +1,202 @@
+"""Main ReachyMiniScript compiler - orchestrates lexing, parsing, semantic analysis, and optimization."""
+
+import logging
+from pathlib import Path
+
+from rmscript.lexer import Lexer
+from rmscript.ir import CompilationResult, CompilationError
+from rmscript.parser import Parser, ParseError
+from rmscript.semantic import SemanticAnalyzer
+from rmscript.optimizer import Optimizer
+
+
+class RMScriptCompiler:
+ """Compiler for ReachyMiniScript language."""
+
+ def __init__(self, log_level: str = "WARNING"):
+ """Initialize compiler."""
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(log_level)
+
+ def compile(self, source: str) -> CompilationResult:
+ """Compile ReachyMiniScript source code into intermediate representation."""
+ result = CompilationResult(
+ name="",
+ description="",
+ source_code=source,
+ )
+
+ try:
+ # Stage 1: Lexical Analysis
+ self.logger.info("Stage 1: Lexical analysis...")
+ lexer = Lexer(source)
+ tokens = lexer.tokenize()
+ self.logger.debug(f"Generated {len(tokens)} tokens")
+
+ # Stage 2: Parsing
+ self.logger.info("Stage 2: Parsing...")
+ parser = Parser(tokens)
+ ast = parser.parse()
+
+ # Use tool_name from AST, or default to "rmscript_tool" if empty
+ result.name = ast.tool_name if ast.tool_name else "rmscript_tool"
+ result.description = ast.description
+
+ self.logger.debug(f"Parsed tool '{result.name}'")
+
+ # Stage 3: Semantic Analysis
+ self.logger.info("Stage 3: Semantic analysis...")
+ analyzer = SemanticAnalyzer()
+ ir = analyzer.analyze(ast)
+
+ result.ir = ir
+ result.errors.extend(analyzer.errors)
+ result.warnings.extend(analyzer.warnings)
+
+ if result.errors:
+ self.logger.error(
+ f"Compilation failed with {len(result.errors)} error(s)"
+ )
+ result.success = False
+ return result
+
+ self.logger.debug(f"Generated {len(ir)} IR actions")
+
+ # Stage 4: Optimization
+ self.logger.info("Stage 4: Optimization...")
+ optimizer = Optimizer()
+ optimized_ir = optimizer.optimize(ir)
+
+ result.ir = optimized_ir
+ self.logger.debug(
+ f"Optimized to {len(optimized_ir)} actions (from {len(ir)})"
+ )
+
+ # Compilation successful
+ result.success = True
+
+ self.logger.info(f"✓ Successfully compiled tool '{result.name}'")
+
+ # Print warnings if any
+ if result.warnings:
+ self.logger.warning(
+ f"Compilation succeeded with {len(result.warnings)} warning(s)"
+ )
+
+ except ParseError as e:
+ # Parser error
+ result.success = False
+ result.errors.append(
+ CompilationError(
+ line=e.token.line, column=e.token.column, message=e.message
+ )
+ )
+ self.logger.error(f"Parse error: {e}")
+
+ except SyntaxError as e:
+ # Lexer error
+ result.success = False
+ result.errors.append(CompilationError(line=1, column=1, message=str(e)))
+ self.logger.error(f"Syntax error: {e}")
+
+ except Exception as e:
+ # Unexpected error
+ result.success = False
+ result.errors.append(
+ CompilationError(line=0, column=0, message=f"Internal error: {e}")
+ )
+ self.logger.exception("Unexpected error during compilation")
+
+ return result
+
+ def compile_file(self, filepath: str) -> CompilationResult:
+ """Compile a rmscript file.
+
+ The tool name is derived from the filename (without .rmscript extension),
+ with spaces replaced by underscores.
+ """
+ try:
+ # Extract tool name from filename
+ filename = Path(filepath).stem # Remove .rmscript extension
+ tool_name = filename.replace(" ", "_") # Replace spaces with underscores
+
+ # Read and compile source
+ with open(filepath, "r") as f:
+ source = f.read()
+
+ result = self.compile(source)
+
+ # Override tool name with filename (from compile(), name is "rmscript_tool")
+ if result.name == "rmscript_tool":
+ result.name = tool_name
+
+ # Store absolute path to source file
+ result.source_file_path = str(Path(filepath).resolve())
+
+ return result
+ except FileNotFoundError:
+ result = CompilationResult(name="", description="", source_code="")
+ result.success = False
+ result.errors.append(
+ CompilationError(
+ line=0, column=0, message=f"File not found: {filepath}"
+ )
+ )
+ return result
+ except Exception as e:
+ result = CompilationResult(name="", description="", source_code="")
+ result.success = False
+ result.errors.append(
+ CompilationError(
+ line=0, column=0, message=f"Error reading file: {e}"
+ )
+ )
+ return result
+
+
+def compile_script(source: str, verbose: bool = False) -> CompilationResult:
+ """Compile ReachyMiniScript source, convenience function."""
+ log_level = "INFO" if verbose else "WARNING"
+ compiler = RMScriptCompiler(log_level=log_level)
+ return compiler.compile(source)
+
+
+def compile_file(filepath: str, verbose: bool = False) -> CompilationResult:
+ """Compile ReachyMiniScript file, convenience function."""
+ log_level = "INFO" if verbose else "WARNING"
+ compiler = RMScriptCompiler(log_level=log_level)
+ return compiler.compile_file(filepath)
+
+
+def verify_script(source: str) -> tuple[bool, list[str]]:
+ """Verify ReachyMiniScript source without generating executable code.
+
+ Args:
+ source: ReachyMiniScript source code to verify
+
+ Returns:
+ Tuple of (is_valid, error_messages)
+ - is_valid: True if compilation succeeds, False otherwise
+ - error_messages: List of error and warning messages (empty if no errors/warnings)
+
+ Example:
+ >>> is_valid, errors = verify_script("look left\\nwait 1s")
+ >>> if not is_valid:
+ ... for error in errors:
+ ... print(error)
+
+ """
+ compiler = RMScriptCompiler(log_level="ERROR")
+ result = compiler.compile(source)
+
+ error_messages = []
+
+ # Collect errors
+ for error in result.errors:
+ error_messages.append(str(error))
+
+ # Collect warnings
+ for warning in result.warnings:
+ error_messages.append(str(warning))
+
+ return result.success, error_messages
diff --git a/rmscript/src/rmscript/constants.py b/rmscript/src/rmscript/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..df47afee3a25ad1d22870c3df7ce9ac8d1e0c369
--- /dev/null
+++ b/rmscript/src/rmscript/constants.py
@@ -0,0 +1,158 @@
+"""Constants and default values for ReachyMiniScript."""
+
+from typing import Dict, List
+
+
+# Default Strengths
+DEFAULT_ANGLE = 30 # degrees (for turn, look, tilt)
+DEFAULT_DISTANCE = 10 # mm (for head translation)
+DEFAULT_ANTENNA_ANGLE = 45 # degrees
+
+# Qualitative Strength Mappings - Context-Aware
+# Different movement types have different physical limits
+
+# For BODY YAW (turn command) - can handle larger angles
+BODY_YAW_VERY_SMALL = 10 # degrees
+BODY_YAW_SMALL = 30 # degrees
+BODY_YAW_MEDIUM = 60 # degrees
+BODY_YAW_LARGE = 90 # degrees
+BODY_YAW_VERY_LARGE = 120 # degrees (safe limit, max is ±160°)
+
+# For HEAD PITCH/ROLL (look up/down, tilt) - limited by cone constraint
+HEAD_PITCH_ROLL_VERY_SMALL = 5 # degrees
+HEAD_PITCH_ROLL_SMALL = 10 # degrees
+HEAD_PITCH_ROLL_MEDIUM = 20 # degrees
+HEAD_PITCH_ROLL_LARGE = 30 # degrees
+HEAD_PITCH_ROLL_VERY_LARGE = 38 # degrees (just under ±40° limit)
+
+# For HEAD YAW (look left/right) - larger range possible
+HEAD_YAW_VERY_SMALL = 5 # degrees
+HEAD_YAW_SMALL = 15 # degrees
+HEAD_YAW_MEDIUM = 30 # degrees
+HEAD_YAW_LARGE = 45 # degrees
+HEAD_YAW_VERY_LARGE = 60 # degrees (respecting ±65° body-head differential)
+
+# For ANTENNAS - safe operating range
+ANTENNA_VERY_SMALL = 10 # degrees
+ANTENNA_SMALL = 30 # degrees
+ANTENNA_MEDIUM = 60 # degrees
+ANTENNA_LARGE = 90 # degrees
+ANTENNA_VERY_LARGE = 110 # degrees (safe limit, under ±120°)
+
+# For HEAD TRANSLATIONS (distances in mm)
+TRANSLATION_VERY_SMALL = 2 # mm
+TRANSLATION_SMALL = 5 # mm
+TRANSLATION_MEDIUM = 10 # mm
+TRANSLATION_LARGE = 20 # mm
+TRANSLATION_VERY_LARGE = 28 # mm (just under ±30mm limit)
+
+# Legacy constants (deprecated - use context-aware versions above)
+VERY_SMALL_ANGLE = HEAD_YAW_VERY_SMALL
+VERY_SMALL_DISTANCE = TRANSLATION_VERY_SMALL
+SMALL_ANGLE = HEAD_YAW_SMALL
+SMALL_DISTANCE = TRANSLATION_SMALL
+MEDIUM_ANGLE = HEAD_YAW_MEDIUM
+MEDIUM_DISTANCE = TRANSLATION_MEDIUM
+LARGE_ANGLE = HEAD_YAW_LARGE
+LARGE_DISTANCE = TRANSLATION_LARGE
+VERY_LARGE_ANGLE = HEAD_YAW_VERY_LARGE
+VERY_LARGE_DISTANCE = TRANSLATION_VERY_LARGE
+
+# Duration Mappings
+DEFAULT_DURATION = 1.0 # seconds
+
+DURATION_KEYWORDS: Dict[str, float] = {
+ "superfast": 0.2,
+ "veryfast": 0.2,
+ "fast": 0.5,
+ "slow": 2.0,
+ "slowly": 2.0,
+ "veryslow": 3.0,
+ "superslow": 3.0,
+}
+
+# Qualitative keywords
+SMALL_KEYWORDS: List[str] = ["little", "slightly", "small", "alittle"]
+MEDIUM_KEYWORDS: List[str] = ["medium", "normal", "regular", "standard", "normally"]
+LARGE_KEYWORDS: List[str] = ["lot", "big", "large", "very", "alot", "huge", "strong", "strongly"]
+VERY_SMALL_KEYWORDS: List[str] = ["minuscule", "mini", "verysmall", "tiny"]
+VERY_LARGE_KEYWORDS: List[str] = ["verybig", "enormous","verylarge","maximum"]
+
+# Direction Synonyms
+CENTER_SYNONYMS: List[str] = ["center", "straight", "forward", "neutral"]
+INWARD_SYNONYMS: List[str] = ["in", "inside", "inward"]
+OUTWARD_SYNONYMS: List[str] = ["out", "outside", "outward"]
+BACKWARD_SYNONYMS: List[str] = ["back", "backward", "backwards"]
+
+# All valid directions (used by lexer for tokenization)
+ALL_DIRECTIONS: List[str] = [
+ "left", "right", "up", "down", "both"
+] + CENTER_SYNONYMS + INWARD_SYNONYMS + OUTWARD_SYNONYMS + BACKWARD_SYNONYMS
+
+# Physical Limits (from robot constraints)
+MAX_BODY_YAW_DEG = 160.0 # degrees
+MAX_HEAD_PITCH_DEG = 40.0 # degrees (cone constraint)
+MAX_HEAD_ROLL_DEG = 40.0 # degrees (cone constraint)
+MAX_HEAD_YAW_DEG = 180.0 # degrees (absolute)
+MAX_HEAD_BODY_YAW_DIFF_DEG = 65.0 # degrees (relative constraint)
+
+MAX_HEAD_TRANSLATION_X_MM = 30.0 # mm
+MAX_HEAD_TRANSLATION_Y_MM = 30.0 # mm
+MAX_HEAD_TRANSLATION_Z_MM = 40.0 # mm (positive)
+MIN_HEAD_TRANSLATION_Z_MM = -20.0 # mm (negative)
+
+MAX_ANTENNA_ANGLE_DEG = 180.0 # degrees
+SAFE_ANTENNA_ANGLE_DEG = 120.0 # recommended maximum for safety
+
+# Movement Keywords
+MOVEMENT_KEYWORDS: List[str] = ["turn", "look", "head", "tilt", "antenna"]
+
+# Control Keywords
+CONTROL_KEYWORDS: List[str] = ["wait", "repeat", "end", "play", "loop", "picture"]
+
+# Sound Playback Mode Keywords
+SOUND_BLOCKING_KEYWORDS: List[str] = ["pause", "fully", "wait", "block", "complete"]
+
+# Valid Directions per Keyword
+TURN_DIRECTIONS: List[str] = ["left", "right"] + CENTER_SYNONYMS
+LOOK_DIRECTIONS: List[str] = ["left", "right", "up", "down"] + CENTER_SYNONYMS
+HEAD_DIRECTIONS: List[str] = ["forward", "left", "right", "up", "down"] + BACKWARD_SYNONYMS
+TILT_DIRECTIONS: List[str] = ["left", "right"] + CENTER_SYNONYMS
+
+# Antenna movements (OLD SYSTEM - kept for backward compatibility if needed)
+ANTENNA_MOVEMENTS: List[str] = ["up", "down"] + INWARD_SYNONYMS + OUTWARD_SYNONYMS
+ANTENNA_MODIFIERS: List[str] = ["left", "right", "both"]
+
+# Antenna clock system (NEW)
+ANTENNA_CLOCK_KEYWORDS: Dict[str, float] = {
+ "high": 0.0, # 0 o'clock = 0°
+ "ext": 3.0, # 3 o'clock = +90° (external)
+ "low": 6.0, # 6 o'clock = ±180°
+ "int": 9.0, # 9 o'clock = -90° (internal)
+}
+
+# Antenna directional keywords (map to clock positions)
+ANTENNA_DIRECTION_KEYWORDS: Dict[str, float] = {
+ "up": 0.0, # Same as high: 0°
+ "right": 3.0, # Same as ext: +90° (external/right)
+ "down": 6.0, # Same as low: ±180°
+ "left": 9.0, # Same as int: -90° (internal/left)
+}
+
+# Direction Sign Conventions (for coordinate system)
+# Note: These match the robot's actual coordinate system
+# Positive values
+POSITIVE_DIRECTIONS = {
+ "left": "yaw", # Positive yaw (verified from robot)
+ "down": "pitch", # Positive pitch (verified from robot)
+ "forward": "x", # Positive X
+}
+
+# Negative values
+NEGATIVE_DIRECTIONS = {
+ "right": "yaw", # Negative yaw (verified from robot)
+ "up": "pitch", # Negative pitch (verified from robot)
+ "back": "x", # Negative X
+ "backward": "x", # Negative X (synonym for back)
+ "backwards": "x", # Negative X (synonym for back)
+}
diff --git a/rmscript/src/rmscript/ir.py b/rmscript/src/rmscript/ir.py
new file mode 100644
index 0000000000000000000000000000000000000000..e08b8a9da28dbd4cf603052eba11580b812adae7
--- /dev/null
+++ b/rmscript/src/rmscript/ir.py
@@ -0,0 +1,89 @@
+"""Intermediate representation and compilation result types for ReachyMiniScript."""
+
+from typing import List, Literal, Optional
+from dataclasses import field, dataclass
+
+import numpy as np
+import numpy.typing as npt
+
+
+@dataclass
+class CompilationError:
+ """Represents a compilation error or warning."""
+
+ line: int
+ column: int = 0
+ message: str = ""
+ severity: Literal["error", "warning"] = "error"
+
+ def __str__(self) -> str:
+ """Format error message for display."""
+ icon = "❌" if self.severity == "error" else "⚠️ "
+ return f"{icon} Line {self.line}: {self.message}"
+
+
+@dataclass
+class IRAction:
+ """Resolved action - all defaults applied, ready to execute."""
+
+ head_pose: Optional[npt.NDArray[np.float64]] = None # 4x4 matrix
+ antennas: Optional[List[float]] = None # [right, left] in radians
+ body_yaw: Optional[float] = None # radians
+ duration: float = 1.0
+ interpolation: str = "minjerk"
+
+ # Metadata for debugging
+ source_line: int = 0
+ original_text: str = ""
+
+
+@dataclass
+class IRWaitAction:
+ """Wait/pause action."""
+
+ duration: float
+ source_line: int = 0
+ original_text: str = ""
+
+
+@dataclass
+class IRPictureAction:
+ """Take a picture action."""
+
+ source_line: int = 0
+ original_text: str = ""
+
+
+@dataclass
+class IRPlaySoundAction:
+ """Play a sound action."""
+
+ sound_name: str # Name of the sound file (without extension)
+ blocking: bool = False # True = wait for sound to finish, False = play in background
+ loop: bool = False # True = loop the sound
+ duration: Optional[float] = None # Duration for looping or limited playback
+ source_line: int = 0
+ original_text: str = ""
+
+
+@dataclass
+class CompilationResult:
+ """Result of ReachyMiniScript compilation."""
+
+ # Tool metadata
+ name: str
+ description: str
+
+ # Compilation results
+ success: bool = False
+ errors: List[CompilationError] = field(default_factory=list)
+ warnings: List[CompilationError] = field(default_factory=list)
+
+ # Source code
+ source_code: str = ""
+ source_file_path: Optional[str] = None
+
+ # Intermediate representation (IR output)
+ ir: List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction] = field(
+ default_factory=list
+ )
diff --git a/rmscript/src/rmscript/lexer.py b/rmscript/src/rmscript/lexer.py
new file mode 100644
index 0000000000000000000000000000000000000000..85bdfc38b270ee4991c84bcea4a3815a4bbdcb23
--- /dev/null
+++ b/rmscript/src/rmscript/lexer.py
@@ -0,0 +1,323 @@
+"""Lexer for ReachyMiniScript - tokenization and indentation handling."""
+
+from enum import Enum, auto
+from typing import List, Optional
+from dataclasses import dataclass
+
+
+class TokenType(Enum):
+ """Token types for ReachyMiniScript."""
+
+ # Keywords
+ KEYWORD_TURN = auto()
+ KEYWORD_LOOK = auto()
+ KEYWORD_HEAD = auto()
+ KEYWORD_TILT = auto()
+ KEYWORD_ANTENNA = auto()
+ KEYWORD_WAIT = auto()
+ KEYWORD_PICTURE = auto()
+ KEYWORD_PLAY = auto()
+ KEYWORD_LOOP = auto()
+ KEYWORD_REPEAT = auto()
+ KEYWORD_END = auto()
+ KEYWORD_DESCRIPTION = auto()
+
+ # Directions
+ DIRECTION = auto() # left, right, up, down, center, etc.
+
+ # Values
+ NUMBER = auto() # 30, 2.5
+ DURATION = auto() # 2s, 0.5s
+ DURATION_KEYWORD = auto() # fast, slow, superfast, superslow
+ QUALITATIVE = auto() # little, lot, etc.
+ ANTENNA_CLOCK = auto() # high, low, ext, int (clock position keywords)
+ SOUND_BLOCKING = auto() # pause, fully, wait, block, complete (for sound playback)
+
+ # Operators
+ AND = auto() # and
+
+ # Structure
+ INDENT = auto()
+ DEDENT = auto()
+ NEWLINE = auto()
+ EOF = auto()
+
+ # Identifiers
+ IDENTIFIER = auto() # For tool names and other identifiers
+
+
+@dataclass
+class Token:
+ """Represents a lexical token."""
+
+ type: TokenType
+ value: str
+ line: int
+ column: int
+
+ def __repr__(self) -> str:
+ """Turn as string representation for debugging."""
+ return f"Token({self.type.name}, {self.value!r}, L{self.line}:C{self.column})"
+
+
+class Lexer:
+ """Tokenizer for ReachyMiniScript with indentation handling."""
+
+ def __init__(self, source: str):
+ """Initialize lexer with source code."""
+ self.source = source
+ self.pos = 0
+ self.line = 1
+ self.column = 1
+ self.tokens: List[Token] = []
+ self.indent_stack: List[int] = [0] # Track indentation levels
+
+ # Keywords (case-insensitive)
+ self.keywords = {
+ "turn": TokenType.KEYWORD_TURN,
+ "look": TokenType.KEYWORD_LOOK,
+ "head": TokenType.KEYWORD_HEAD,
+ "tilt": TokenType.KEYWORD_TILT,
+ "antenna": TokenType.KEYWORD_ANTENNA,
+ "wait": TokenType.KEYWORD_WAIT,
+ "picture": TokenType.KEYWORD_PICTURE,
+ "play": TokenType.KEYWORD_PLAY,
+ "loop": TokenType.KEYWORD_LOOP,
+ "repeat": TokenType.KEYWORD_REPEAT,
+ "end": TokenType.KEYWORD_END,
+ "description": TokenType.KEYWORD_DESCRIPTION,
+ }
+
+ # Directions - import from constants
+ from rmscript.constants import ALL_DIRECTIONS
+ self.directions = set(ALL_DIRECTIONS)
+
+ # Duration keywords - import from constants
+ from rmscript.constants import DURATION_KEYWORDS
+ self.duration_keywords = set(DURATION_KEYWORDS.keys())
+
+ # Qualitative strength - import from constants
+ from rmscript.constants import (
+ LARGE_KEYWORDS,
+ SMALL_KEYWORDS,
+ MEDIUM_KEYWORDS,
+ VERY_LARGE_KEYWORDS,
+ VERY_SMALL_KEYWORDS,
+ )
+ self.qualitative_keywords = (
+ set(SMALL_KEYWORDS)
+ | set(MEDIUM_KEYWORDS)
+ | set(LARGE_KEYWORDS)
+ | set(VERY_SMALL_KEYWORDS)
+ | set(VERY_LARGE_KEYWORDS)
+ )
+
+ # Antenna clock keywords
+ self.antenna_clock_keywords = {"high", "low", "ext", "int"}
+
+ # Sound blocking keywords (for "play sound pause/fully")
+ from rmscript.constants import SOUND_BLOCKING_KEYWORDS
+ self.sound_blocking_keywords = set(SOUND_BLOCKING_KEYWORDS)
+
+ def error(self, message: str) -> Exception:
+ """Create a lexer error with position information."""
+ return SyntaxError(f"Line {self.line}, Column {self.column}: {message}")
+
+ def peek(self, offset: int = 0) -> Optional[str]:
+ """Peek at character at current position + offset."""
+ pos = self.pos + offset
+ if pos < len(self.source):
+ return self.source[pos]
+ return None
+
+ def advance(self) -> Optional[str]:
+ """Advance position and return current character."""
+ if self.pos >= len(self.source):
+ return None
+
+ char = self.source[self.pos]
+ self.pos += 1
+
+ if char == "\n":
+ self.line += 1
+ self.column = 1
+ else:
+ self.column += 1
+
+ return char
+
+ def skip_whitespace_inline(self) -> None:
+ """Skip spaces and tabs but not newlines."""
+ while self.peek() in (" ", "\t"):
+ self.advance()
+
+ def skip_comment(self) -> None:
+ """Skip comment (from # to end of line)."""
+ if self.peek() == "#":
+ while self.peek() and self.peek() != "\n":
+ self.advance()
+
+ def read_number(self) -> Token:
+ """Read a number token (integer or float)."""
+ start_col = self.column
+ num_str = ""
+
+ while (ch := self.peek()) is not None and (ch.isdigit() or ch == "."):
+ num_str += self.advance() # type: ignore
+
+ # Check if followed by 's' for duration
+ if self.peek() == "s":
+ self.advance()
+ return Token(
+ TokenType.DURATION, num_str + "s", self.line, start_col
+ ) # Keep 's' in value
+
+ return Token(TokenType.NUMBER, num_str, self.line, start_col)
+
+ def read_identifier(self) -> Token:
+ """Read an identifier or keyword."""
+ start_col = self.column
+ ident = ""
+
+ while (ch := self.peek()) is not None and (ch.isalnum() or ch == "_"):
+ ident += self.advance() # type: ignore
+
+ ident_lower = ident.lower()
+
+ # Check if it's a keyword
+ if ident_lower in self.keywords:
+ return Token(self.keywords[ident_lower], ident, self.line, start_col)
+
+ # Check if it's a direction
+ if ident_lower in self.directions:
+ return Token(TokenType.DIRECTION, ident_lower, self.line, start_col)
+
+ # Check if it's a duration keyword
+ if ident_lower in self.duration_keywords:
+ return Token(TokenType.DURATION_KEYWORD, ident_lower, self.line, start_col)
+
+ # Check if it's a qualitative keyword
+ if ident_lower in self.qualitative_keywords:
+ return Token(TokenType.QUALITATIVE, ident_lower, self.line, start_col)
+
+ # Check if it's an antenna clock keyword
+ if ident_lower in self.antenna_clock_keywords:
+ return Token(TokenType.ANTENNA_CLOCK, ident_lower, self.line, start_col)
+
+ # Check if it's a sound blocking keyword
+ if ident_lower in self.sound_blocking_keywords:
+ return Token(TokenType.SOUND_BLOCKING, ident_lower, self.line, start_col)
+
+ # Check for 'and'
+ if ident_lower == "and":
+ return Token(TokenType.AND, ident_lower, self.line, start_col)
+
+ # Otherwise it's a generic identifier
+ return Token(TokenType.IDENTIFIER, ident, self.line, start_col)
+
+ def handle_indentation(self, indent_level: int) -> List[Token]:
+ """Generate INDENT/DEDENT tokens based on indentation level."""
+ tokens = []
+
+ if indent_level > self.indent_stack[-1]:
+ # Indent
+ self.indent_stack.append(indent_level)
+ tokens.append(Token(TokenType.INDENT, "", self.line, 1))
+
+ elif indent_level < self.indent_stack[-1]:
+ # Dedent (possibly multiple levels)
+ while self.indent_stack and indent_level < self.indent_stack[-1]:
+ self.indent_stack.pop()
+ tokens.append(Token(TokenType.DEDENT, "", self.line, 1))
+
+ # Check if we dedented to a valid level
+ if not self.indent_stack or indent_level != self.indent_stack[-1]:
+ raise self.error(f"Inconsistent indentation (level {indent_level})")
+
+ return tokens
+
+ def tokenize(self) -> List[Token]:
+ """Tokenize the entire source code."""
+ tokens = []
+ at_line_start = True
+
+ while self.pos < len(self.source):
+ # Handle indentation at start of line BEFORE skipping whitespace
+ if at_line_start:
+ # Count indentation
+ indent_level = 0
+ temp_pos = self.pos
+ while temp_pos < len(self.source) and self.source[temp_pos] in (
+ " ",
+ "\t",
+ ):
+ if self.source[temp_pos] == " ":
+ indent_level += 1
+ else: # tab
+ indent_level += 4 # Treat tab as 4 spaces
+ temp_pos += 1
+
+ # Check if this is a blank line or comment-only line
+ if (
+ temp_pos >= len(self.source)
+ or self.source[temp_pos] == "\n"
+ or self.source[temp_pos] == "#"
+ ):
+ # Skip blank/comment lines - consume the line
+ while self.peek() and self.peek() != "\n":
+ self.advance()
+ if self.peek() == "\n":
+ self.advance()
+ continue
+
+ # Generate INDENT/DEDENT tokens
+ indent_tokens = self.handle_indentation(indent_level)
+ tokens.extend(indent_tokens)
+
+ at_line_start = False
+
+ # Skip inline whitespace
+ self.skip_whitespace_inline()
+
+ # Skip comments
+ self.skip_comment()
+
+ # Check for EOF
+ if self.pos >= len(self.source):
+ break
+
+ # Handle newlines
+ if self.peek() == "\n":
+ self.advance()
+ tokens.append(Token(TokenType.NEWLINE, "\\n", self.line - 1, 1))
+ at_line_start = True
+ continue
+
+ # Read numbers
+ if (ch := self.peek()) is not None and ch.isdigit():
+ tokens.append(self.read_number())
+ continue
+
+ # Read identifiers/keywords
+ if (ch := self.peek()) is not None and (ch.isalpha() or ch == "_"):
+ tokens.append(self.read_identifier())
+ continue
+
+ # Unknown character
+ raise self.error(f"Unexpected character: {self.peek()!r}")
+
+ # Close any remaining indentation levels
+ while len(self.indent_stack) > 1:
+ self.indent_stack.pop()
+ tokens.append(Token(TokenType.DEDENT, "", self.line, 1))
+
+ # Add EOF token
+ tokens.append(Token(TokenType.EOF, "", self.line, self.column))
+
+ return tokens
+
+
+def tokenize(source: str) -> List[Token]:
+ """Tokenize source code, convenience function."""
+ lexer = Lexer(source)
+ return lexer.tokenize()
diff --git a/rmscript/src/rmscript/optimizer.py b/rmscript/src/rmscript/optimizer.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f90c77c4f147430b4e032b9b5bbdf7c1d31a9ac
--- /dev/null
+++ b/rmscript/src/rmscript/optimizer.py
@@ -0,0 +1,64 @@
+"""Optimizer for ReachyMiniScript - further optimize IR for execution."""
+
+from typing import List
+
+from rmscript.ir import IRAction, IRWaitAction, IRPictureAction, IRPlaySoundAction
+
+
+class Optimizer:
+ """Optimizes intermediate representation."""
+
+ def optimize(self, ir: List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction]) -> List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction]:
+ """Optimize IR.
+
+ Current optimizations:
+ - Merge consecutive wait actions
+ - Remove no-op actions
+
+ Future optimizations:
+ - Combine compatible actions with same duration
+ - Minimize movement time
+ """
+ optimized: List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction] = []
+
+ i = 0
+ while i < len(ir):
+ action = ir[i]
+
+ # Merge consecutive waits
+ if isinstance(action, IRWaitAction):
+ total_wait = action.duration
+ j = i + 1
+ while j < len(ir):
+ next_action = ir[j]
+ if isinstance(next_action, IRWaitAction):
+ total_wait += next_action.duration
+ j += 1
+ else:
+ break
+
+ optimized.append(
+ IRWaitAction(
+ duration=total_wait,
+ source_line=action.source_line,
+ original_text=f"wait {total_wait}s",
+ )
+ )
+ i = j
+ continue
+
+ # Remove no-op actions (no actual movement)
+ if isinstance(action, IRAction):
+ if (
+ action.head_pose is None
+ and action.antennas is None
+ and action.body_yaw is None
+ ):
+ # Skip this action - it does nothing
+ i += 1
+ continue
+
+ optimized.append(action)
+ i += 1
+
+ return optimized
diff --git a/rmscript/src/rmscript/parser.py b/rmscript/src/rmscript/parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..80118856be0e9634b153710433d9c4e6a97c7501
--- /dev/null
+++ b/rmscript/src/rmscript/parser.py
@@ -0,0 +1,441 @@
+"""Parser for ReachyMiniScript - builds Abstract Syntax Tree from tokens."""
+
+from typing import List, Optional
+
+from rmscript.lexer import Token, TokenType
+from rmscript.ast_nodes import (
+ Program,
+ Statement,
+ ActionChain,
+ RepeatBlock,
+ SingleAction,
+ WaitStatement,
+ PictureStatement,
+ PlaySoundStatement,
+)
+from rmscript.constants import (
+ HEAD_DIRECTIONS,
+ LOOK_DIRECTIONS,
+ TILT_DIRECTIONS,
+ TURN_DIRECTIONS,
+ ANTENNA_MODIFIERS,
+ ANTENNA_DIRECTION_KEYWORDS,
+)
+
+
+class ParseError(Exception):
+ """Parser error with position information."""
+
+ def __init__(self, message: str, token: Token):
+ """Initialize parse error."""
+ self.message = message
+ self.token = token
+ super().__init__(f"Line {token.line}: {message}")
+
+
+class Parser:
+ """Parser for ReachyMiniScript."""
+
+ def __init__(self, tokens: List[Token]):
+ """Initialize parser with token stream."""
+ self.tokens = tokens
+ self.pos = 0
+
+ def error(self, message: str) -> ParseError:
+ """Create parse error at current position."""
+ return ParseError(message, self.current())
+
+ def current(self) -> Token:
+ """Get current token."""
+ if self.pos < len(self.tokens):
+ return self.tokens[self.pos]
+ return self.tokens[-1] # Return EOF token
+
+ def peek(self, offset: int = 0) -> Token:
+ """Peek at token at current position + offset."""
+ pos = self.pos + offset
+ if pos < len(self.tokens):
+ return self.tokens[pos]
+ return self.tokens[-1] # Return EOF token
+
+ def advance(self) -> Token:
+ """Consume and return current token."""
+ token = self.current()
+ if self.pos < len(self.tokens) - 1:
+ self.pos += 1
+ return token
+
+ def expect(self, token_type: TokenType) -> Token:
+ """Consume token of expected type or raise error."""
+ token = self.current()
+ if token.type != token_type:
+ raise self.error(
+ f"Expected {token_type.name}, got {token.type.name} ({token.value!r})"
+ )
+ return self.advance()
+
+ def skip_newlines(self) -> None:
+ """Skip any newline tokens."""
+ while self.current().type == TokenType.NEWLINE:
+ self.advance()
+
+ def parse(self) -> Program:
+ """Parse entire program."""
+ program = Program()
+
+ self.skip_newlines()
+
+ # Parse optional DESCRIPTION header
+ if self.current().type == TokenType.KEYWORD_DESCRIPTION:
+ self.advance() # Skip DESCRIPTION keyword
+
+ # Read the rest of the line as the description
+ description_parts = []
+ while self.current().type not in (TokenType.NEWLINE, TokenType.EOF):
+ description_parts.append(self.current().value)
+ self.advance()
+ program.description = " ".join(description_parts)
+ self.skip_newlines()
+
+ # Note: tool_name will be set from filename by the compiler
+ program.tool_name = ""
+
+ # Parse statements
+ program.statements = self.parse_statements()
+
+ return program
+
+ def parse_statements(self) -> List[Statement]:
+ """Parse a list of statements."""
+ statements = []
+
+ while self.current().type not in (
+ TokenType.EOF,
+ TokenType.DEDENT,
+ TokenType.KEYWORD_END,
+ ):
+ self.skip_newlines()
+
+ if self.current().type in (
+ TokenType.EOF,
+ TokenType.DEDENT,
+ TokenType.KEYWORD_END,
+ ):
+ break
+
+ stmt = self.parse_statement()
+ if stmt:
+ statements.append(stmt)
+
+ self.skip_newlines()
+
+ return statements
+
+ def parse_statement(self) -> Optional[Statement]:
+ """Parse a single statement."""
+ token = self.current()
+
+ # Check for repeat block
+ if token.type == TokenType.KEYWORD_REPEAT:
+ return self.parse_repeat_block()
+
+ # Check for wait command
+ if token.type == TokenType.KEYWORD_WAIT:
+ return self.parse_wait()
+
+ # Check for picture command
+ if token.type == TokenType.KEYWORD_PICTURE:
+ return self.parse_picture()
+
+ # Check for play command
+ if token.type == TokenType.KEYWORD_PLAY:
+ return self.parse_play()
+
+ # Check for loop command
+ if token.type == TokenType.KEYWORD_LOOP:
+ return self.parse_loop()
+
+ # Check for movement keywords
+ if token.type in (
+ TokenType.KEYWORD_TURN,
+ TokenType.KEYWORD_LOOK,
+ TokenType.KEYWORD_HEAD,
+ TokenType.KEYWORD_TILT,
+ TokenType.KEYWORD_ANTENNA,
+ ):
+ return self.parse_action_chain()
+
+ # Unknown statement
+ if token.type != TokenType.NEWLINE:
+ raise self.error(f"Unexpected token: {token.value!r}")
+
+ return None
+
+ def parse_wait(self) -> WaitStatement:
+ """Parse wait command."""
+ token = self.current()
+ self.advance() # consume 'wait'
+
+ wait = WaitStatement(line=token.line, column=token.column)
+
+ # Parse duration
+ if self.current().type == TokenType.DURATION:
+ duration_str = self.current().value.rstrip("s")
+ wait.duration = float(duration_str)
+ self.advance()
+ elif self.current().type == TokenType.DURATION_KEYWORD:
+ wait.duration_keyword = self.current().value
+ self.advance()
+ elif self.current().type == TokenType.NUMBER:
+ wait.duration = float(self.current().value)
+ self.advance()
+ # Require 's' suffix for consistency
+ if self.current().value != "s":
+ raise self.error(
+ f"Expected 's' after wait duration (e.g., 'wait {wait.duration}s')"
+ )
+ self.advance()
+ else:
+ raise self.error("Expected duration after 'wait' (e.g., 'wait 1s')")
+
+ return wait
+
+ def parse_picture(self) -> PictureStatement:
+ """Parse picture command."""
+ token = self.current()
+ self.advance() # consume 'picture'
+
+ return PictureStatement(line=token.line, column=token.column)
+
+ def parse_play(self) -> PlaySoundStatement:
+ """Parse play command: 'play soundname', 'play soundname pause', or 'play soundname 10s'."""
+ token = self.current()
+ self.advance() # consume 'play'
+
+ play = PlaySoundStatement(line=token.line, column=token.column)
+
+ # Expect sound name (identifier)
+ if self.current().type != TokenType.IDENTIFIER:
+ raise self.error(f"Expected sound name after 'play', got '{self.current().value}'")
+
+ play.sound_name = self.current().value
+ self.advance()
+
+ # Check for optional duration or blocking modifier
+ if self.current().type == TokenType.DURATION:
+ # Duration specified (e.g., "play mysound 10s")
+ duration_str = self.current().value.rstrip("s")
+ play.duration = float(duration_str)
+ play.blocking = True # Duration implies blocking
+ self.advance()
+ elif self.current().type == TokenType.SOUND_BLOCKING:
+ # Blocking modifier (pause, fully, wait, block, complete)
+ play.blocking = True
+ self.advance()
+
+ return play
+
+ def parse_loop(self) -> PlaySoundStatement:
+ """Parse loop command: 'loop soundname' or 'loop soundname 10s'."""
+ token = self.current()
+ self.advance() # consume 'loop'
+
+ play = PlaySoundStatement(line=token.line, column=token.column, loop=True, blocking=True)
+
+ # Expect sound name (identifier)
+ if self.current().type != TokenType.IDENTIFIER:
+ raise self.error(f"Expected sound name after 'loop', got '{self.current().value}'")
+
+ play.sound_name = self.current().value
+ self.advance()
+
+ # Check for optional duration (default to 10s if not specified)
+ if self.current().type == TokenType.DURATION:
+ duration_str = self.current().value.rstrip("s")
+ play.duration = float(duration_str)
+ self.advance()
+ else:
+ # Default duration for loop is 10s
+ play.duration = 10.0
+
+ return play
+
+ def parse_repeat_block(self) -> RepeatBlock:
+ """Parse repeat block."""
+ token = self.current()
+ self.advance() # consume 'repeat'
+
+ # Parse count
+ if self.current().type != TokenType.NUMBER:
+ raise self.error("Expected number after 'repeat'")
+
+ count = int(float(self.current().value))
+ self.advance()
+
+ self.skip_newlines()
+
+ # Expect indent
+ if self.current().type != TokenType.INDENT:
+ raise self.error("Expected indented block after 'repeat'")
+ self.advance()
+
+ # Parse body
+ body = self.parse_statements()
+
+ # Expect dedent (this closes the block)
+ if self.current().type == TokenType.DEDENT:
+ self.advance()
+ # Note: 'end' keyword is no longer required - indentation defines the block
+
+ return RepeatBlock(count=count, body=body, line=token.line, column=token.column)
+
+ def parse_action_chain(self) -> ActionChain:
+ """Parse action chain (one or more actions connected by 'and')."""
+ chain = ActionChain()
+ chain.line = self.current().line
+ chain.column = self.current().column
+
+ # Parse first action
+ first_action = self.parse_single_action()
+ chain.actions.append(first_action)
+
+ # Parse additional actions connected by 'and'
+ while self.current().type == TokenType.AND:
+ self.advance() # consume 'and'
+
+ # Check if next token is a keyword
+ next_action = self.parse_single_action(
+ previous_keyword=first_action.keyword
+ )
+ chain.actions.append(next_action)
+
+ return chain
+
+ def parse_single_action(
+ self, previous_keyword: Optional[str] = None
+ ) -> SingleAction:
+ """Parse a single action.
+
+ Args:
+ previous_keyword: If provided and no keyword is present, reuse this keyword.
+
+ """
+ token = self.current()
+ action = SingleAction(keyword="", line=token.line, column=token.column)
+
+ # Check if we have a movement keyword
+ if token.type in (
+ TokenType.KEYWORD_TURN,
+ TokenType.KEYWORD_LOOK,
+ TokenType.KEYWORD_HEAD,
+ TokenType.KEYWORD_TILT,
+ TokenType.KEYWORD_ANTENNA,
+ ):
+ action.keyword = token.value.lower()
+ self.advance()
+ elif previous_keyword:
+ # Check if current token is a control keyword (not allowed after 'and')
+ if token.type in (TokenType.KEYWORD_PICTURE, TokenType.KEYWORD_PLAY, TokenType.KEYWORD_LOOP, TokenType.KEYWORD_WAIT):
+ raise self.error(
+ f"Cannot combine movement with '{token.value}' using 'and'. "
+ f"Use separate lines instead."
+ )
+ # Keyword reuse after 'and'
+ action.keyword = previous_keyword
+ else:
+ raise self.error(f"Expected movement keyword, got {token.value!r}")
+
+ # Parse antenna modifier and clock position if applicable
+ if action.keyword == "antenna":
+ # Antenna modifier is now REQUIRED (left, right, both)
+ if (
+ self.current().type == TokenType.DIRECTION
+ and self.current().value in ANTENNA_MODIFIERS
+ ):
+ action.antenna_modifier = self.current().value
+ self.advance()
+ else:
+ raise self.error(
+ f"Antenna command requires a modifier (left/right/both), got '{self.current().value}'"
+ )
+
+ # Now parse clock position (number, clock keyword, or directional keyword)
+ if self.current().type == TokenType.NUMBER:
+ # Clock position as number (0-12)
+ clock_pos = float(self.current().value)
+ if clock_pos < 0 or clock_pos > 12:
+ raise self.error(
+ f"Antenna clock position must be between 0 and 12, got {clock_pos}"
+ )
+ action.strength = clock_pos # Store as strength for now
+ action.direction = "clock" # Special marker
+ self.advance()
+ elif self.current().type == TokenType.ANTENNA_CLOCK:
+ # Clock keyword (high/low/ext/int)
+ action.direction = self.current().value # Store keyword
+ self.advance()
+ elif (
+ self.current().type == TokenType.DIRECTION
+ and self.current().value in ANTENNA_DIRECTION_KEYWORDS
+ ):
+ # Directional keyword (up/down/left/right)
+ action.direction = self.current().value # Store keyword
+ self.advance()
+ else:
+ raise self.error(
+ f"Antenna command requires a position (0-12, high/low/ext/int, or up/down/left/right), got '{self.current().value}'"
+ )
+
+ # Parse direction for non-antenna keywords
+ elif self.current().type == TokenType.DIRECTION:
+ direction = self.current().value
+
+ # Validate direction for keyword
+ if action.keyword == "turn" and direction not in TURN_DIRECTIONS:
+ raise self.error(
+ f"Invalid direction '{direction}' for 'turn' (use left/right/center)"
+ )
+ elif action.keyword == "look" and direction not in LOOK_DIRECTIONS:
+ raise self.error(
+ f"Invalid direction '{direction}' for 'look' (use left/right/up/down/center)"
+ )
+ elif action.keyword == "head" and direction not in HEAD_DIRECTIONS:
+ raise self.error(
+ f"Invalid direction '{direction}' for 'head' (use forward/back/left/right/up/down)"
+ )
+ elif action.keyword == "tilt" and direction not in TILT_DIRECTIONS:
+ raise self.error(
+ f"Invalid direction '{direction}' for 'tilt' (use left/right/center)"
+ )
+
+ action.direction = direction
+ self.advance()
+
+ # Parse optional parameters (strength and/or duration, in any order)
+ while self.current().type in (
+ TokenType.NUMBER,
+ TokenType.DURATION,
+ TokenType.DURATION_KEYWORD,
+ TokenType.QUALITATIVE,
+ ):
+ if self.current().type == TokenType.NUMBER:
+ action.strength = float(self.current().value)
+ self.advance()
+ elif self.current().type == TokenType.DURATION:
+ duration_str = self.current().value.rstrip("s")
+ action.duration = float(duration_str)
+ self.advance()
+ elif self.current().type == TokenType.DURATION_KEYWORD:
+ action.duration_keyword = self.current().value
+ self.advance()
+ elif self.current().type == TokenType.QUALITATIVE:
+ action.strength_qualitative = self.current().value
+ self.advance()
+
+ return action
+
+
+def parse(tokens: List[Token]) -> Program:
+ """Parse tokens into AST, convenience function."""
+ parser = Parser(tokens)
+ return parser.parse()
diff --git a/rmscript/src/rmscript/semantic.py b/rmscript/src/rmscript/semantic.py
new file mode 100644
index 0000000000000000000000000000000000000000..7314849b7f488776bbe7967c17437d026e688e74
--- /dev/null
+++ b/rmscript/src/rmscript/semantic.py
@@ -0,0 +1,535 @@
+"""Semantic analysis for ReachyMiniScript - validation, defaults, and IR generation."""
+
+from typing import List
+
+import numpy as np
+
+from reachy_mini.utils import create_head_pose
+from rmscript.ir import (
+ IRAction,
+ IRWaitAction,
+ IRPictureAction,
+ IRPlaySoundAction,
+ CompilationError,
+)
+from rmscript.ast_nodes import (
+ Program,
+ Statement,
+ ActionChain,
+ RepeatBlock,
+ SingleAction,
+ WaitStatement,
+ PictureStatement,
+ PlaySoundStatement,
+)
+from rmscript.constants import (
+ # Legacy angle/distance constants
+ ANTENNA_LARGE,
+ ANTENNA_SMALL,
+ DEFAULT_ANGLE,
+ ANTENNA_MEDIUM,
+ BODY_YAW_LARGE,
+ BODY_YAW_SMALL,
+ HEAD_YAW_LARGE,
+ HEAD_YAW_SMALL,
+ LARGE_KEYWORDS,
+ SMALL_KEYWORDS,
+ BODY_YAW_MEDIUM,
+ CENTER_SYNONYMS,
+ HEAD_YAW_MEDIUM,
+ MEDIUM_KEYWORDS,
+ DEFAULT_DISTANCE,
+ BACKWARD_SYNONYMS,
+ # Other constants
+ DEFAULT_DURATION,
+ MAX_BODY_YAW_DEG,
+ DURATION_KEYWORDS,
+ MAX_HEAD_ROLL_DEG,
+ TRANSLATION_LARGE,
+ TRANSLATION_SMALL,
+ ANTENNA_VERY_LARGE,
+ ANTENNA_VERY_SMALL,
+ MAX_HEAD_PITCH_DEG,
+ TRANSLATION_MEDIUM,
+ BODY_YAW_VERY_LARGE,
+ # Context-aware qualitative constants
+ BODY_YAW_VERY_SMALL,
+ HEAD_YAW_VERY_LARGE,
+ HEAD_YAW_VERY_SMALL,
+ VERY_LARGE_KEYWORDS,
+ VERY_SMALL_KEYWORDS,
+ DEFAULT_ANTENNA_ANGLE,
+ HEAD_PITCH_ROLL_LARGE,
+ HEAD_PITCH_ROLL_SMALL,
+ MAX_ANTENNA_ANGLE_DEG,
+ ANTENNA_CLOCK_KEYWORDS,
+ HEAD_PITCH_ROLL_MEDIUM,
+ SAFE_ANTENNA_ANGLE_DEG,
+ TRANSLATION_VERY_LARGE,
+ TRANSLATION_VERY_SMALL,
+ MAX_HEAD_TRANSLATION_X_MM,
+ MAX_HEAD_TRANSLATION_Y_MM,
+ MAX_HEAD_TRANSLATION_Z_MM,
+ MIN_HEAD_TRANSLATION_Z_MM,
+ ANTENNA_DIRECTION_KEYWORDS,
+ HEAD_PITCH_ROLL_VERY_LARGE,
+ HEAD_PITCH_ROLL_VERY_SMALL,
+)
+
+
+class SemanticAnalyzer:
+ """Analyzes AST and generates intermediate representation."""
+
+ def __init__(self) -> None:
+ """Initialize semantic analyzer."""
+ self.errors: List[CompilationError] = []
+ self.warnings: List[CompilationError] = []
+
+ @staticmethod
+ def clock_to_angle(clock_position: float) -> float:
+ """Convert clock position (0-12) to angle in degrees.
+
+ Args:
+ clock_position: Clock position from 0 to 12
+
+ Returns:
+ Angle in degrees, normalized to ±180°
+
+ Examples:
+ 0 -> 0° (straight up)
+ 3 -> 90° (to the right)
+ 6 -> 180° (straight down)
+ 9 -> -90° (to the left)
+
+ """
+ # Convert clock position to angle: position * 30° (360/12)
+ angle = clock_position * 30.0
+
+ # Normalize to ±180° range
+ if angle > 180:
+ angle = angle - 360
+
+ return angle
+
+ def error(self, line: int, message: str) -> None:
+ """Add an error."""
+ self.errors.append(CompilationError(line=line, message=message))
+
+ def warn(self, line: int, message: str) -> None:
+ """Add a warning."""
+ self.warnings.append(
+ CompilationError(line=line, message=message, severity="warning")
+ )
+
+ def analyze(self, program: Program) -> List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction]:
+ """Analyze program and generate IR."""
+ ir: List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction] = []
+
+ for statement in program.statements:
+ ir.extend(self.analyze_statement(statement))
+
+ return ir
+
+ def analyze_statement(self, stmt: Statement) -> List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction]:
+ """Analyze a single statement."""
+ if isinstance(stmt, WaitStatement):
+ return [self.analyze_wait(stmt)]
+ elif isinstance(stmt, PictureStatement):
+ return [self.analyze_picture(stmt)]
+ elif isinstance(stmt, PlaySoundStatement):
+ return [self.analyze_play_sound(stmt)]
+ elif isinstance(stmt, ActionChain):
+ return [self.analyze_action_chain(stmt)]
+ elif isinstance(stmt, RepeatBlock):
+ return self.analyze_repeat(stmt)
+ else:
+ self.error(0, f"Unknown statement type: {type(stmt)}")
+ return []
+
+ def analyze_wait(self, wait: WaitStatement) -> IRWaitAction:
+ """Analyze wait statement."""
+ # Resolve duration
+ duration = DEFAULT_DURATION
+ if wait.duration is not None:
+ duration = wait.duration
+ elif wait.duration_keyword is not None:
+ duration = DURATION_KEYWORDS.get(wait.duration_keyword, DEFAULT_DURATION)
+
+ return IRWaitAction(
+ duration=duration, source_line=wait.line, original_text="wait"
+ )
+
+ def analyze_picture(self, picture: PictureStatement) -> IRPictureAction:
+ """Analyze picture statement."""
+ return IRPictureAction(
+ source_line=picture.line, original_text="picture"
+ )
+
+ def analyze_play_sound(self, play: PlaySoundStatement) -> IRPlaySoundAction:
+ """Analyze play sound statement."""
+ return IRPlaySoundAction(
+ sound_name=play.sound_name,
+ blocking=play.blocking,
+ loop=play.loop,
+ duration=play.duration,
+ source_line=play.line,
+ original_text=f"{'loop' if play.loop else 'play'} {play.sound_name}"
+ )
+
+ def analyze_repeat(self, repeat: RepeatBlock) -> List[IRAction | IRWaitAction | IRPictureAction | IRPlaySoundAction]:
+ """Analyze repeat block - expand it into IR."""
+ if repeat.count < 0:
+ self.error(repeat.line, f"Repeat count cannot be negative: {repeat.count}")
+ return []
+
+ if repeat.count == 0:
+ self.warn(
+ repeat.line,
+ "Repeat count is 0, block will not execute",
+ )
+ return []
+
+ # Analyze body once
+ body_ir = []
+ for stmt in repeat.body:
+ body_ir.extend(self.analyze_statement(stmt))
+
+ # Expand repeat
+ expanded = []
+ for _ in range(repeat.count):
+ expanded.extend(body_ir)
+
+ return expanded
+
+ def analyze_action_chain(self, chain: ActionChain) -> IRAction:
+ """Analyze action chain and merge into single IRAction."""
+ # Resolve each action
+ resolved_actions = [self.resolve_action(action) for action in chain.actions]
+
+ # Merge all actions into a single IRAction
+ merged = self.merge_actions(resolved_actions, chain.line)
+
+ return merged
+
+ def resolve_action(self, action: SingleAction) -> "ResolvedAction":
+ """Resolve a single action - apply defaults and convert qualitative."""
+ # Determine strength
+ strength = self.resolve_strength(action)
+
+ # Determine duration
+ duration = self.resolve_duration(action)
+
+ # Check ranges and issue warnings
+ self.validate_ranges(action, strength)
+
+ return ResolvedAction(
+ keyword=action.keyword,
+ direction=action.direction or "",
+ antenna_modifier=action.antenna_modifier or "both",
+ strength=strength,
+ duration=duration,
+ line=action.line,
+ )
+
+ def _get_qualitative_values(self, action: SingleAction) -> tuple[float, float, float, float, float, float]:
+ """Get context-aware qualitative values based on movement type.
+
+ Returns tuple of (very_small, small, medium, large, very_large, default).
+ """
+ if action.keyword == "head":
+ # Head translations (distances in mm)
+ return (TRANSLATION_VERY_SMALL, TRANSLATION_SMALL, TRANSLATION_MEDIUM,
+ TRANSLATION_LARGE, TRANSLATION_VERY_LARGE, DEFAULT_DISTANCE)
+
+ elif action.keyword == "turn":
+ # Body yaw rotation
+ return (BODY_YAW_VERY_SMALL, BODY_YAW_SMALL, BODY_YAW_MEDIUM,
+ BODY_YAW_LARGE, BODY_YAW_VERY_LARGE, DEFAULT_ANGLE)
+
+ elif action.keyword == "antenna":
+ # Antenna rotations
+ return (ANTENNA_VERY_SMALL, ANTENNA_SMALL, ANTENNA_MEDIUM,
+ ANTENNA_LARGE, ANTENNA_VERY_LARGE, DEFAULT_ANTENNA_ANGLE)
+
+ elif action.keyword == "tilt":
+ # Head roll - limited by cone constraint
+ return (HEAD_PITCH_ROLL_VERY_SMALL, HEAD_PITCH_ROLL_SMALL, HEAD_PITCH_ROLL_MEDIUM,
+ HEAD_PITCH_ROLL_LARGE, HEAD_PITCH_ROLL_VERY_LARGE, DEFAULT_ANGLE)
+
+ elif action.keyword == "look":
+ # Head pitch/yaw - depends on direction
+ if action.direction in ["up", "down"]:
+ # Pitch - limited by cone constraint
+ return (HEAD_PITCH_ROLL_VERY_SMALL, HEAD_PITCH_ROLL_SMALL, HEAD_PITCH_ROLL_MEDIUM,
+ HEAD_PITCH_ROLL_LARGE, HEAD_PITCH_ROLL_VERY_LARGE, DEFAULT_ANGLE)
+ else:
+ # Yaw (left/right) - larger range
+ return (HEAD_YAW_VERY_SMALL, HEAD_YAW_SMALL, HEAD_YAW_MEDIUM,
+ HEAD_YAW_LARGE, HEAD_YAW_VERY_LARGE, DEFAULT_ANGLE)
+
+ else:
+ # Fallback to head yaw values
+ return (HEAD_YAW_VERY_SMALL, HEAD_YAW_SMALL, HEAD_YAW_MEDIUM,
+ HEAD_YAW_LARGE, HEAD_YAW_VERY_LARGE, DEFAULT_ANGLE)
+
+ def resolve_strength(self, action: SingleAction) -> float:
+ """Resolve strength parameter with context-aware qualitative mapping."""
+ # If quantitative specified, use it
+ if action.strength is not None:
+ if action.strength_qualitative is not None:
+ self.warn(
+ action.line,
+ f"Both qualitative '{action.strength_qualitative}' and quantitative '{action.strength}' strength specified, using {action.strength}",
+ )
+ return action.strength
+
+ # Get context-aware values
+ very_small, small, medium, large, very_large, default = self._get_qualitative_values(action)
+
+ # If qualitative specified, convert it using context-aware values
+ if action.strength_qualitative is not None:
+ if action.strength_qualitative in VERY_SMALL_KEYWORDS:
+ return float(very_small)
+ elif action.strength_qualitative in SMALL_KEYWORDS:
+ return float(small)
+ elif action.strength_qualitative in MEDIUM_KEYWORDS:
+ return float(medium)
+ elif action.strength_qualitative in LARGE_KEYWORDS:
+ return float(large)
+ elif action.strength_qualitative in VERY_LARGE_KEYWORDS:
+ return float(very_large)
+
+ # Use context-aware default
+ return float(default)
+
+ def resolve_duration(self, action: SingleAction) -> float:
+ """Resolve duration parameter."""
+ if action.duration is not None:
+ if action.duration < 0.1:
+ self.warn(
+ action.line,
+ f"Very short duration ({action.duration}s) may cause jerky motion",
+ )
+ return action.duration
+
+ if action.duration_keyword is not None:
+ return DURATION_KEYWORDS.get(action.duration_keyword, DEFAULT_DURATION)
+
+ return DEFAULT_DURATION
+
+ def validate_ranges(self, action: SingleAction, strength: float) -> None:
+ """Validate ranges and issue warnings."""
+ if action.keyword == "turn":
+ if abs(strength) > MAX_BODY_YAW_DEG:
+ self.warn(
+ action.line,
+ f"Body yaw {strength}° exceeds safe range (±{MAX_BODY_YAW_DEG}°), will be clamped",
+ )
+
+ elif action.keyword == "look":
+ if action.direction in ["up", "down"]: # pitch
+ if abs(strength) > MAX_HEAD_PITCH_DEG:
+ self.warn(
+ action.line,
+ f"Head pitch {strength}° exceeds limit (±{MAX_HEAD_PITCH_DEG}°), will be clamped",
+ )
+
+ elif action.keyword == "tilt":
+ if abs(strength) > MAX_HEAD_ROLL_DEG:
+ self.warn(
+ action.line,
+ f"Head roll {strength}° exceeds limit (±{MAX_HEAD_ROLL_DEG}°), will be clamped",
+ )
+
+ elif action.keyword == "head":
+ if action.direction in ["forward", "back"]:
+ if abs(strength) > MAX_HEAD_TRANSLATION_X_MM:
+ self.warn(
+ action.line,
+ f"Head X translation {strength}mm exceeds typical range (±{MAX_HEAD_TRANSLATION_X_MM}mm)",
+ )
+ elif action.direction in ["left", "right"]:
+ if abs(strength) > MAX_HEAD_TRANSLATION_Y_MM:
+ self.warn(
+ action.line,
+ f"Head Y translation {strength}mm exceeds typical range (±{MAX_HEAD_TRANSLATION_Y_MM}mm)",
+ )
+ elif action.direction == "up":
+ if strength > MAX_HEAD_TRANSLATION_Z_MM:
+ self.warn(
+ action.line,
+ f"Head Z translation {strength}mm exceeds typical range ({MAX_HEAD_TRANSLATION_Z_MM}mm max)",
+ )
+ elif action.direction == "down":
+ if strength > abs(MIN_HEAD_TRANSLATION_Z_MM):
+ self.warn(
+ action.line,
+ f"Head Z translation {strength}mm exceeds typical range ({MIN_HEAD_TRANSLATION_Z_MM}mm min)",
+ )
+
+ elif action.keyword == "antenna":
+ if abs(strength) > MAX_ANTENNA_ANGLE_DEG:
+ self.warn(
+ action.line,
+ f"Antenna angle {strength}° exceeds maximum (±{MAX_ANTENNA_ANGLE_DEG}°), will be clamped",
+ )
+ elif abs(strength) > SAFE_ANTENNA_ANGLE_DEG:
+ self.warn(
+ action.line,
+ f"Antenna angle {strength}° exceeds recommended safe range (±{SAFE_ANTENNA_ANGLE_DEG}°), may cause collision",
+ )
+
+ def merge_actions(
+ self, actions: List["ResolvedAction"], line: int
+ ) -> IRAction: # noqa: F821
+ """Merge multiple actions into a single IRAction IR node."""
+ # Extract parameters
+ head_pose_params = {"x": 0.0, "y": 0.0, "z": 0.0, "roll": 0.0, "pitch": 0.0, "yaw": 0.0}
+ has_head_movement = False
+
+ antennas = [0.0, 0.0] # [right, left]
+ has_antenna_movement = False
+
+ body_yaw = 0.0
+ has_body_yaw = False
+
+ # Use the maximum duration
+ max_duration = max((a.duration for a in actions), default=DEFAULT_DURATION)
+
+ for action in actions:
+ if action.keyword == "turn":
+ has_body_yaw = True
+ has_head_movement = True # Head rotates with body
+ if action.direction in CENTER_SYNONYMS:
+ body_yaw = 0.0
+ head_pose_params["yaw"] = 0.0
+ elif action.direction == "left":
+ body_yaw += action.strength # LEFT = positive yaw
+ head_pose_params["yaw"] += action.strength # Head follows body
+ elif action.direction == "right":
+ body_yaw -= action.strength # RIGHT = negative yaw
+ head_pose_params["yaw"] -= action.strength # Head follows body
+
+ elif action.keyword == "look":
+ has_head_movement = True
+ if action.direction in CENTER_SYNONYMS:
+ head_pose_params["yaw"] = 0.0
+ head_pose_params["pitch"] = 0.0
+ elif action.direction == "left":
+ head_pose_params["yaw"] += action.strength # LEFT = positive yaw
+ elif action.direction == "right":
+ head_pose_params["yaw"] -= action.strength # RIGHT = negative yaw
+ elif action.direction == "up":
+ head_pose_params["pitch"] -= action.strength # UP = negative pitch
+ elif action.direction == "down":
+ head_pose_params["pitch"] += action.strength # DOWN = positive pitch
+
+ elif action.keyword == "head":
+ has_head_movement = True
+ if action.direction == "forward":
+ head_pose_params["x"] += action.strength
+ elif action.direction in BACKWARD_SYNONYMS:
+ head_pose_params["x"] -= action.strength
+ elif action.direction == "left":
+ head_pose_params["y"] += action.strength
+ elif action.direction == "right":
+ head_pose_params["y"] -= action.strength
+ elif action.direction == "up":
+ head_pose_params["z"] += action.strength
+ elif action.direction == "down":
+ head_pose_params["z"] -= action.strength
+
+ elif action.keyword == "tilt":
+ has_head_movement = True
+ if action.direction in CENTER_SYNONYMS:
+ head_pose_params["roll"] = 0.0
+ elif action.direction == "left":
+ head_pose_params["roll"] += action.strength
+ elif action.direction == "right":
+ head_pose_params["roll"] -= action.strength
+
+ elif action.keyword == "antenna":
+ has_antenna_movement = True
+
+ # Determine which antenna(s) to move
+ # Note: antennas array is [left_antenna, right_antenna]
+ if action.antenna_modifier == "both":
+ left_idx, right_idx = 0, 1
+ elif action.antenna_modifier == "right":
+ left_idx, right_idx = None, 1
+ elif action.antenna_modifier == "left":
+ left_idx, right_idx = 0, None
+ else:
+ left_idx, right_idx = 0, 1 # default to both
+
+ # Convert clock position to angle
+ # If direction is "clock", the clock position is stored in strength
+ # If direction is a clock keyword (high/low/ext/int), look it up
+ if action.direction == "clock":
+ # Clock position as number (already in strength)
+ clock_pos = action.strength
+ elif action.direction in ANTENNA_CLOCK_KEYWORDS:
+ # Clock keyword - look up the position
+ clock_pos = ANTENNA_CLOCK_KEYWORDS[action.direction]
+ elif action.direction in ANTENNA_DIRECTION_KEYWORDS:
+ # Directional keyword (up/down/left/right) - look up the position
+ clock_pos = ANTENNA_DIRECTION_KEYWORDS[action.direction]
+ else:
+ # Shouldn't happen due to parser validation, but fallback
+ clock_pos = 0.0
+
+ # Convert clock position to angle
+ angle = self.clock_to_angle(clock_pos)
+
+ # Set antenna positions (same angle for specified antenna(s))
+ if right_idx is not None:
+ antennas[right_idx] = angle
+ if left_idx is not None:
+ antennas[left_idx] = angle
+
+ # Build IRAction IR node
+ result = IRAction(
+ source_line=line,
+ duration=max_duration,
+ )
+
+ if has_head_movement:
+ result.head_pose = create_head_pose(
+ x=head_pose_params["x"],
+ y=head_pose_params["y"],
+ z=head_pose_params["z"],
+ roll=head_pose_params["roll"],
+ pitch=head_pose_params["pitch"],
+ yaw=head_pose_params["yaw"],
+ mm=True,
+ degrees=True,
+ )
+
+ if has_antenna_movement:
+ result.antennas = [np.deg2rad(antennas[0]), np.deg2rad(antennas[1])]
+
+ if has_body_yaw:
+ result.body_yaw = np.deg2rad(body_yaw)
+
+ return result
+
+
+class ResolvedAction:
+ """Temporary structure for resolved action parameters."""
+
+ def __init__(
+ self,
+ keyword: str,
+ direction: str,
+ antenna_modifier: str,
+ strength: float,
+ duration: float,
+ line: int,
+ ):
+ """Initialize resolved action."""
+ self.keyword = keyword
+ self.direction = direction
+ self.antenna_modifier = antenna_modifier
+ self.strength = strength
+ self.duration = duration
+ self.line = line
diff --git a/rmscript/uv.lock b/rmscript/uv.lock
new file mode 100644
index 0000000000000000000000000000000000000000..adf377fbe7c06797b885ff0f61078b4dff7280bd
--- /dev/null
+++ b/rmscript/uv.lock
@@ -0,0 +1,2179 @@
+version = 1
+revision = 3
+requires-python = ">=3.11"
+resolution-markers = [
+ "python_full_version >= '3.12' and sys_platform == 'darwin'",
+ "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" },
+ { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" },
+ { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" },
+ { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" },
+ { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" },
+ { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" },
+ { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" },
+ { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" },
+ { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" },
+ { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" },
+ { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" },
+ { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" },
+ { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" },
+ { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" },
+ { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" },
+ { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" },
+ { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" },
+ { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" },
+ { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" },
+ { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" },
+ { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" },
+ { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" },
+ { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" },
+ { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" },
+ { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" },
+ { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" },
+ { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
+]
+
+[[package]]
+name = "annotated-doc"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" },
+]
+
+[[package]]
+name = "asgiref"
+version = "3.11.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/b9/4db2509eabd14b4a8c71d1b24c8d5734c52b8560a7b1e1a8b56c8d25568b/asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4", size = 37969, upload-time = "2025-11-19T15:32:20.106Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096, upload-time = "2025-11-19T15:32:19.004Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "25.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.11.12"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "cv2-enumerate-cameras"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/cf/a6eb5fa54d7dfc87c93a6161aef57527c6ef4d8239be7cef10d13fb9efd6/cv2_enumerate_cameras-1.3.0.tar.gz", hash = "sha256:795d8005b0e92ca117e1b8c4849c752edb39b7e2a1818b2d7ac4463aa26caac1", size = 12455, upload-time = "2025-09-12T04:42:33.549Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/38/cc570b8579a2be52d0a3f6dba7afad536bc6d41cfc44108727ef7d8b185a/cv2_enumerate_cameras-1.3.0-cp32-abi3-win32.whl", hash = "sha256:bd24be3c2df42b88da59e9f6029b97484fc8468678a2be3900aa717835c40f6a", size = 20406, upload-time = "2025-09-12T04:42:28.463Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/ef/0a6e095462e90d12358ef68653c7ea4d86dd6f5dd8f433279ecb5745e9ca/cv2_enumerate_cameras-1.3.0-cp32-abi3-win_amd64.whl", hash = "sha256:ef892cc7d92dec3ad0f696fa592a9d5461e593698048f84e877353cffcfc486f", size = 21296, upload-time = "2025-09-12T04:42:30.87Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/d5/6a6b74c92b6901d7077eb67284010354d90762702ed3451ea5d6297ef692/cv2_enumerate_cameras-1.3.0-py3-none-any.whl", hash = "sha256:7a67d8cc98fd112c3df51ff699c6953884e96088ced2ee05191bdeaaa2e1e847", size = 11571, upload-time = "2025-09-12T04:42:32.272Z" },
+]
+
+[[package]]
+name = "eclipse-zenoh"
+version = "1.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/3b/15bb190da421536b28de45438ad328925f203b5b274c5e92a7dcc6f7ac14/eclipse_zenoh-1.6.2.tar.gz", hash = "sha256:852b649a9d1c482edb690759311d5322fa10e5388ba6dec507d35151b48ceb35", size = 132916, upload-time = "2025-10-17T20:11:01.322Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/13/0e6511e1f007686084beff96d960407edc8303a0aac267689087e1be3e11/eclipse_zenoh-1.6.2-cp39-abi3-linux_armv6l.whl", hash = "sha256:b311c5e4626cc2faad367ef0edbb09079dafefc004531f135cafd22af48f362e", size = 9454073, upload-time = "2025-10-17T20:10:46.806Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/84/f36e224fb7eb05927e55ea7cd2ee000f7a9254d194f7ab539627a1635dd3/eclipse_zenoh-1.6.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:13d0b10c09fbd4233356a38b11cf008112acefda308a4dd4feb3c8e74c674065", size = 17565641, upload-time = "2025-10-17T20:10:49.061Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/7d/98194e5f4f369eb7f49024d7d4536342636bee216f191cbb111c7ee86764/eclipse_zenoh-1.6.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:29f081cf6ba5eee7c72606cab1e534e6e6a825c08899529c176d7a6fa1cff6c4", size = 8984387, upload-time = "2025-10-17T20:10:50.736Z" },
+ { url = "https://files.pythonhosted.org/packages/04/17/0b550ec08095471bd099258ea23685f569dba79394fa22dd25c232d75cae/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c1c332334ec4bceb0856472146684cfb4230e55c0409cba6b369e34c87a5f5cb", size = 9337753, upload-time = "2025-10-17T20:10:52.696Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/bc/9c2196cc742ad0ccdcc9e942bea3d627f70e5a17708ab412e05bbca07180/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cf678ed2578b954cf8d1863c897c9d4404d9bac1c9560dfdb9a300bb488e01", size = 10147324, upload-time = "2025-10-17T20:10:55.234Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/81/7ac634430be1fd15aea65d6e849f7d73d0a8cf285dd74e8e39cd7b7ea2e2/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5db633632e3da9c63ae70e219f2a84dca3dcb08125fb9aef9f11602104790afb", size = 9626850, upload-time = "2025-10-17T20:10:56.785Z" },
+ { url = "https://files.pythonhosted.org/packages/29/fe/c5b2e65e0c2800b4e1a02b388e0ddf7c3b71f478757bb6a294c021d49e84/eclipse_zenoh-1.6.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e7f9bf3849567631caa3bdeecf2edce66461b1ae511f28dfafea6089022d02e1", size = 9487136, upload-time = "2025-10-17T20:10:58.359Z" },
+ { url = "https://files.pythonhosted.org/packages/77/93/4df169108c4313d0f476bb9abd7d76496059e8bfcc3a98168aa26cc73b00/eclipse_zenoh-1.6.2-cp39-abi3-win_amd64.whl", hash = "sha256:f59f75f0d733daf5059843037e63bb62dd5268809d041a150bcfd8f8d62b9b08", size = 7805746, upload-time = "2025-10-17T20:11:00.257Z" },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.122.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b2/de/3ee97a4f6ffef1fb70bf20561e4f88531633bb5045dc6cebc0f8471f764d/fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b", size = 346436, upload-time = "2025-11-24T19:17:47.95Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/93/aa8072af4ff37b795f6bbf43dcaf61115f40f49935c7dbb180c9afc3f421/fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67", size = 110671, upload-time = "2025-11-24T19:17:45.96Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" },
+ { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" },
+ { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" },
+ { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" },
+ { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" },
+ { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" },
+ { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" },
+ { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" },
+ { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" },
+ { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" },
+ { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" },
+ { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" },
+ { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" },
+ { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" },
+ { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" },
+ { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
+]
+
+[[package]]
+name = "fsspec"
+version = "2025.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "hf-xet"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" },
+ { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" },
+ { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" },
+ { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" },
+ { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" },
+ { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" },
+ { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" },
+]
+
+[[package]]
+name = "httptools"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" },
+ { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" },
+ { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" },
+ { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" },
+ { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" },
+ { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" },
+ { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" },
+ { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" },
+ { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.34.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "libusb-package"
+version = "1.0.26.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "importlib-resources" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/bf/3fe9d322e2dcd0437ae2bd6a039117965702ed473ca59d2d6a1c39838009/libusb_package-1.0.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:60e15d7d3e4aab31794da95641bc28c4ffec9e24f50891ce33f75794b8f531f3", size = 63864, upload-time = "2025-04-01T12:59:14.567Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/70/df0348c11e6aaead4a66cc59840e102ddf64baf8e4b2c1ad5cff1ca83554/libusb_package-1.0.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d93a6137609cf72dc5db69bc337ddf96520231e395beeff69fa77a923090003", size = 59502, upload-time = "2025-04-01T12:59:15.863Z" },
+ { url = "https://files.pythonhosted.org/packages/27/67/6c84eebc9fcdf7f26704b5d32b51b3ee5bf4e9090d61286941257bdc8702/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fafb69c5fd42b241fbd20493d014328c507d34e1b7ceb883a20ef14565b26898", size = 70247, upload-time = "2025-04-01T14:53:07.606Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/b4/cbcc42ca4b3d8778bf081b96e6e6288a437d82a4cc4e9b982bef40a88856/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c206cd8a30565a0cede3ba426929e70a37e7b769e41a5ac7f00ca6737dc5d", size = 74537, upload-time = "2025-04-01T14:53:08.61Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/0f/fd203fb1fa5eda1d446f345d84205f23533767e6ef837a7c77a2599d5783/libusb_package-1.0.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a2041331c087d5887969405837f86c8422120fe9ba3e6faa44bf4810f07b71", size = 70653, upload-time = "2025-04-01T14:53:09.576Z" },
+ { url = "https://files.pythonhosted.org/packages/79/ef/dcc682cb4b29c4d4cdb23df65825c6276753184f6a7b4338c54a59a54c20/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48b536a1279ee0dbf70b898cffd16cd661774d2c8bbec8ff7178a5bc20196af3", size = 71859, upload-time = "2025-04-01T14:53:10.987Z" },
+ { url = "https://files.pythonhosted.org/packages/62/4d/323d5ac4ba339362e4b148c291fbc6e7ee04c6395d5fec967b32432db5c5/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f273e33ff1810242f81ea3a0286e25887d99d99019ba83e08be0d1ca456cc05", size = 76476, upload-time = "2025-04-01T14:53:12.019Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3b/506db7f6cbe5dc2f38c14b272b8faf4d43e5559ac99d4dce1a41026ec925/libusb_package-1.0.26.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67476093601e1ea58a6130426795b906acd8d18d51c84e29a3a69548a5dfcf5d", size = 71037, upload-time = "2025-04-01T14:53:13.42Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/40/2538763c06e07bbbe0a5c8830779ef1ed1cea845264a91973bf31b9ecce5/libusb_package-1.0.26.3-cp311-cp311-win32.whl", hash = "sha256:8f3eed2852ee4f08847a221749a98d0f4f3962f8bed967e2253327db1171ba60", size = 77642, upload-time = "2025-04-01T12:58:03.106Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/46/0cd5ea91c5bbe6293c0936c96915051e31750f72e9556718af666af3fe45/libusb_package-1.0.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:b48b5f5b17c7ac5e315e233f9ee801f730aac6183eb53a3226b01245d7bcfe00", size = 90592, upload-time = "2025-04-01T12:58:04.103Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/f6/83e13936b5799360eae8f0e31b5b298dd092451b91136d7cd13852777954/libusb_package-1.0.26.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c9404298485762a4e73b416e8a3208d33aa3274fb9b870c2a1cacba7e2918f19", size = 62045, upload-time = "2025-04-01T12:59:16.817Z" },
+ { url = "https://files.pythonhosted.org/packages/33/97/86ed73880b6734c9383be5f34061b541e8fe5bd0303580b1f5abe2962d58/libusb_package-1.0.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8126f6711318dad4cb2805ea20cd47b895a847207087d8fdb032e082dd7a2e24", size = 59502, upload-time = "2025-04-01T12:59:17.72Z" },
+ { url = "https://files.pythonhosted.org/packages/95/f7/27b67b8fe63450abf0b0b66aacf75d5d64cdf30317e214409ceb534f34b4/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11c219366e4a2368117b9a9807261f3506b5623531f8b8ce41af5bbaec8156a0", size = 70247, upload-time = "2025-04-01T14:53:14.387Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/11/613543f9c6dab5a82eefd0c78d52d08b5d9eb93a0362151fbedf74b32541/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8809a50d8ab84297344c54e862027090c0d73b14abef843a8b5f783313f49457", size = 74537, upload-time = "2025-04-01T14:53:15.345Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/43/5a2331615693b56221a902869fb2094d9a0b9a764a8706c8ba16e915f77c/libusb_package-1.0.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a83067c3dfdbb3856badb4532eaea22e8502b52ce4245f5ab46acf93d7fbd471", size = 70652, upload-time = "2025-04-01T14:53:16.319Z" },
+ { url = "https://files.pythonhosted.org/packages/44/1a/186d4ec86421b69feb45e214edb5301fbcb9e8dc9df963678aeff1a447d5/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b56be087ea9cde8e50fb02740a4f0cefb6f63c61ac2e7812a9244487614a3973", size = 71860, upload-time = "2025-04-01T14:53:17.87Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/3c/8cebdad822d7bfcb683a77d5fd113fbc6f72516cfb7c1c3a274fefafa8e9/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea0f6bf40e54b1671e763e40c9dbed46bf7f596a4cd98b7c827e147f176d8c97", size = 76476, upload-time = "2025-04-01T14:53:19.202Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5f/30c625b6c4ecd14871644c1d16e97d7c971f82a0f87a9cfa81022f85bcfc/libusb_package-1.0.26.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b40f77df991c6db8621de9575504886eca03a00277e521a4d64b66cbef8f6997", size = 71037, upload-time = "2025-04-01T14:53:21.359Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/e9/3aa3ff3242867b7f22ee3ce28d0e93ff88547f170ca1b8a6edc59660d974/libusb_package-1.0.26.3-cp312-cp312-win32.whl", hash = "sha256:6eee99c9fde137443869c8604d0c01b2127a9545ebc59d06a3376cf1d891e786", size = 77642, upload-time = "2025-04-01T12:58:05.471Z" },
+ { url = "https://files.pythonhosted.org/packages/15/0e/913ddb1849f828fc385438874c34541939d9b06c0e5616f48f24cddd24de/libusb_package-1.0.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:5e09c0b6b3cd475841cffe78e46e91df58f0c6c02ea105ea1a4d0755a07c8006", size = 90593, upload-time = "2025-04-01T12:58:06.798Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/b8/23bc7f3f53b4a5b1027c721ec3eb42324ca1ec56355f0d0851307adc7c6c/libusb_package-1.0.26.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:04c4505e2ca68d3dc6938f116ff9bf82daffb06c1a97aba08293a84715a998da", size = 62045, upload-time = "2025-04-01T12:59:18.698Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/f8/e3be96d0604070488ddc5ce5af1976992e1f4a00e6441c94edf807f274d5/libusb_package-1.0.26.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4961cdb3c622aa9f858d3e4f99a58ce5e822a97c22abc77040fd806cb5fa4c66", size = 59502, upload-time = "2025-04-01T12:59:19.632Z" },
+ { url = "https://files.pythonhosted.org/packages/24/d5/df1508df5e6776ac8a09a2858991df29bc96ea6a0d1f90240b1c4d59b45d/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16182670e0c23235521b447054c5a01600bd8f1eed3bb08eedbb0d9f8a43249f", size = 70247, upload-time = "2025-04-01T14:53:22.328Z" },
+ { url = "https://files.pythonhosted.org/packages/65/01/4cc9eed12b9214c088cfa8055ece3b1db970404400be9d7e3dda68d198f2/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75ea57b2cc903d28ec1d4b909902df442cbf21949d80d5b3d8b9dac36ac45d1a", size = 74537, upload-time = "2025-04-01T14:53:23.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/83/9eb317f706f588f4b6679bddb8abee3b115ce53dc3fa560cca59910f8807/libusb_package-1.0.26.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d30b51b128ef5112fff73268b4696fea00b5676b3f39a5ee859bd76cb3ace5", size = 70651, upload-time = "2025-04-01T14:53:24.33Z" },
+ { url = "https://files.pythonhosted.org/packages/22/49/85d3b307b4a20cf0150ab381e6e0385e5b78cb5dede8bade0a2d655d3fd3/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c098dcfcfa8000cab42f33e19628c8fdb16111670db381048b2993651f2413b", size = 71860, upload-time = "2025-04-01T14:53:25.752Z" },
+ { url = "https://files.pythonhosted.org/packages/da/7a/2271a5ae542d9036d9254415ae745d5c5d01a08d56d13054b2439bf9d392/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:93169aeab0657255fe6c9f757cf408f559db13827a1d122fc89239994d7d51f1", size = 76477, upload-time = "2025-04-01T14:53:27.564Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9d/d06d53994bb164564ec142ef631a4afa31e324994cf223f169ecca127f3a/libusb_package-1.0.26.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:63257653ee1ee06aa836e942f4bb89a1d7a0c6ae3d6183647a9011e585ffa1e3", size = 71036, upload-time = "2025-04-01T14:53:29.011Z" },
+ { url = "https://files.pythonhosted.org/packages/32/3d/97f775a1d582548b1eb2a42444c58813e5fd93d568fc3b9ace59f64df527/libusb_package-1.0.26.3-cp313-cp313-win32.whl", hash = "sha256:05db4cc801db2e6373a808725748a701509f9450fecf393fbebab61c45d50b50", size = 77642, upload-time = "2025-04-01T12:58:07.774Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c4/d5234607697ca60593fbef88428a154317ac31f5c58ee23337b8a9360e91/libusb_package-1.0.26.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cd4aec825dac2b4fa5d23b37f6d72e63a1127987e5a073dabeb7b73528623a3", size = 90593, upload-time = "2025-04-01T12:58:08.676Z" },
+]
+
+[[package]]
+name = "log-throttling"
+version = "0.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/be/09fda383d82a7b91f12ff5002bc909e101769c802ff3b1d2cb9f533d1a2f/log-throttling-0.0.3.tar.gz", hash = "sha256:df714e1448cd9cb4c916cf668ab88323032e61ece733548708d996ecf63190b9", size = 3533, upload-time = "2022-03-08T16:29:23.674Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/6e/995ebad4ab08250b4afe1d91fe3086b863ed9bb74539d90485250a50f08a/log_throttling-0.0.3-py3-none-any.whl", hash = "sha256:a82508f489dd44a60b737a0339c784dd8ee2b9db16bdcf587a8ae1012a7f6ffc", size = 4081, upload-time = "2022-03-08T16:29:21.443Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
+ { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "6.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" },
+ { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" },
+ { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" },
+ { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" },
+ { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" },
+ { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" },
+ { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" },
+ { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" },
+ { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" },
+ { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" },
+ { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" },
+ { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" },
+ { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" },
+ { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" },
+ { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" },
+ { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" },
+ { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" },
+ { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" },
+ { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" },
+ { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" },
+ { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" },
+ { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" },
+ { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" },
+ { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" },
+ { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" },
+ { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" },
+ { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" },
+ { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" },
+ { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" },
+ { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" },
+ { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" },
+ { url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" },
+ { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" },
+ { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" },
+ { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" },
+ { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" },
+ { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" },
+ { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" },
+ { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" },
+ { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" },
+ { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" },
+ { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" },
+ { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" },
+ { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" },
+ { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" },
+ { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" },
+ { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" },
+ { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" },
+ { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" },
+ { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" },
+ { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" },
+ { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" },
+ { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" },
+ { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" },
+ { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" },
+ { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" },
+ { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" },
+ { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" },
+ { url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" },
+ { url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" },
+]
+
+[[package]]
+name = "opencv-python"
+version = "4.11.0.86"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322, upload-time = "2025-01-16T13:52:25.887Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197, upload-time = "2025-01-16T13:55:21.222Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439, upload-time = "2025-01-16T13:51:35.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597, upload-time = "2025-01-16T13:52:08.836Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337, upload-time = "2025-01-16T13:52:13.549Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044, upload-time = "2025-01-16T13:52:21.928Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
+[[package]]
+name = "pip"
+version = "25.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "propcache"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" },
+ { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" },
+ { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" },
+ { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" },
+ { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" },
+ { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" },
+ { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" },
+ { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" },
+ { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" },
+ { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" },
+ { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" },
+ { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" },
+ { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" },
+ { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" },
+ { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" },
+ { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" },
+ { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" },
+ { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" },
+ { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" },
+ { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" },
+ { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" },
+ { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" },
+ { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" },
+ { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" },
+ { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" },
+ { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" },
+ { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
+]
+
+[[package]]
+name = "psutil"
+version = "7.1.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/93/0c49e776b8734fef56ec9c5c57f923922f2cf0497d62e0f419465f28f3d0/psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc", size = 239751, upload-time = "2025-11-02T12:25:58.161Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8d/b31e39c769e70780f007969815195a55c81a63efebdd4dbe9e7a113adb2f/psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0", size = 240368, upload-time = "2025-11-02T12:26:00.491Z" },
+ { url = "https://files.pythonhosted.org/packages/62/61/23fd4acc3c9eebbf6b6c78bcd89e5d020cfde4acf0a9233e9d4e3fa698b4/psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7", size = 287134, upload-time = "2025-11-02T12:26:02.613Z" },
+ { url = "https://files.pythonhosted.org/packages/30/1c/f921a009ea9ceb51aa355cb0cc118f68d354db36eae18174bab63affb3e6/psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251", size = 289904, upload-time = "2025-11-02T12:26:05.207Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/82/62d68066e13e46a5116df187d319d1724b3f437ddd0f958756fc052677f4/psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa", size = 249642, upload-time = "2025-11-02T12:26:07.447Z" },
+ { url = "https://files.pythonhosted.org/packages/df/ad/c1cd5fe965c14a0392112f68362cfceb5230819dbb5b1888950d18a11d9f/psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee", size = 245518, upload-time = "2025-11-02T12:26:09.719Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/bb/6670bded3e3236eb4287c7bcdc167e9fae6e1e9286e437f7111caed2f909/psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353", size = 239843, upload-time = "2025-11-02T12:26:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/66/853d50e75a38c9a7370ddbeefabdd3d3116b9c31ef94dc92c6729bc36bec/psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b", size = 240369, upload-time = "2025-11-02T12:26:14.358Z" },
+ { url = "https://files.pythonhosted.org/packages/41/bd/313aba97cb5bfb26916dc29cf0646cbe4dd6a89ca69e8c6edce654876d39/psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9", size = 288210, upload-time = "2025-11-02T12:26:16.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/fa/76e3c06e760927a0cfb5705eb38164254de34e9bd86db656d4dbaa228b04/psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f", size = 291182, upload-time = "2025-11-02T12:26:18.848Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/1d/5774a91607035ee5078b8fd747686ebec28a962f178712de100d00b78a32/psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7", size = 250466, upload-time = "2025-11-02T12:26:21.183Z" },
+ { url = "https://files.pythonhosted.org/packages/00/ca/e426584bacb43a5cb1ac91fae1937f478cd8fbe5e4ff96574e698a2c77cd/psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264", size = 245756, upload-time = "2025-11-02T12:26:23.148Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" },
+ { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.23"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.12.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.41.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pyobjc-core"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/95/df/d2b290708e9da86d6e7a9a2a2022b91915cf2e712a5a82e306cb6ee99792/pyobjc_core-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c918ebca280925e7fcb14c5c43ce12dcb9574a33cccb889be7c8c17f3bcce8b6", size = 671263, upload-time = "2025-11-14T09:31:35.231Z" },
+ { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" },
+ { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" },
+ { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-avfoundation"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cd/42/c026ab308edc2ed5582d8b4b93da6b15d1b6557c0086914a4aabedd1f032/pyobjc_framework_avfoundation-12.1.tar.gz", hash = "sha256:eda0bb60be380f9ba2344600c4231dd58a3efafa99fdc65d3673ecfbb83f6fcb", size = 310047, upload-time = "2025-11-14T10:09:40.069Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/5a/4ef36b309138840ff8cd85364f66c29e27023f291004c335a99f6e87e599/pyobjc_framework_avfoundation-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82cc2c2d9ab6cc04feeb4700ff251d00f1fcafff573c63d4e87168ff80adb926", size = 83328, upload-time = "2025-11-14T09:37:40.808Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/00/ca471e5dd33f040f69320832e45415d00440260bf7f8221a9df4c4662659/pyobjc_framework_avfoundation-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bf634f89265b4d93126153200d885b6de4859ed6b3bc65e69ff75540bc398406", size = 83375, upload-time = "2025-11-14T09:37:47.262Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/d4/ade88067deff45858b457648dd82c9363977eb1915efd257232cd06bdac1/pyobjc_framework_avfoundation-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f8ac7f7e0884ac8f12009cdb9d4fefc2f269294ab2ccfd84520a560859b69cec", size = 83413, upload-time = "2025-11-14T09:37:53.759Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/3a/fa699d748d6351fa0aeca656ea2f9eacc36e31203dfa56bc13c8a3d26d7d/pyobjc_framework_avfoundation-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:51aba2c6816badfb1fb5a2de1b68b33a23f065bf9e3b99d46ede0c8c774ac7a4", size = 83860, upload-time = "2025-11-14T09:38:00.051Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/65/a79cf3b8935a78329ac1107056b91868a581096a90ab6ddff5fd28db4947/pyobjc_framework_avfoundation-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9a3ffd1ae90bd72dbcf2875aa9254369e805b904140362a7338ebf1af54201a6", size = 83629, upload-time = "2025-11-14T09:38:06.697Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/03/4125204a17cd7b4de1fdfc38b280a47d0d8f8691a4ee306ebb41b58ff030/pyobjc_framework_avfoundation-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:394c99876b9a38db4851ddf8146db363556895c12e9c711ccd3c3f907ac8e273", size = 83962, upload-time = "2025-11-14T09:38:13.153Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-cocoa"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/07/5760735c0fffc65107e648eaf7e0991f46da442ac4493501be5380e6d9d4/pyobjc_framework_cocoa-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f52228bcf38da64b77328787967d464e28b981492b33a7675585141e1b0a01e6", size = 383812, upload-time = "2025-11-14T09:40:53.169Z" },
+ { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" },
+ { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" },
+ { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" },
+ { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coreaudio"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/84/d1/0b884c5564ab952ff5daa949128c64815300556019c1bba0cf2ca752a1a0/pyobjc_framework_coreaudio-12.1.tar.gz", hash = "sha256:a9e72925fcc1795430496ce0bffd4ddaa92c22460a10308a7283ade830089fe1", size = 75077, upload-time = "2025-11-14T10:13:22.345Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/25/491ff549fd9a40be4416793d335bff1911d3d1d1e1635e3b0defbd2cf585/pyobjc_framework_coreaudio-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a452de6b509fa4a20160c0410b72330ac871696cd80237883955a5b3a4de8f2a", size = 35327, upload-time = "2025-11-14T09:43:32.523Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/48/05b5192122e23140cf583eac99ccc5bf615591d6ff76483ba986c38ee750/pyobjc_framework_coreaudio-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a5ad6309779663f846ab36fe6c49647e470b7e08473c3e48b4f004017bdb68a4", size = 36908, upload-time = "2025-11-14T09:43:36.108Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/ce/45808618fefc760e2948c363e0a3402ff77690c8934609cd07b19bc5b15f/pyobjc_framework_coreaudio-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3d8ef424850c8ae2146f963afaec6c4f5bf0c2e412871e68fb6ecfb209b8376f", size = 36935, upload-time = "2025-11-14T09:43:39.414Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/f6/0d74d9464bfb4f39451abf745174ec0c4d5c5ebf1c2fcb7556263ae3f75a/pyobjc_framework_coreaudio-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6552624df39dbc68ff9328f244ba56f59234ecbde8455db1e617a71bc4f3dd3a", size = 38390, upload-time = "2025-11-14T09:43:43.194Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/f2/c5ca32d01c9d892bf189cfe9b17deaf996db3b4013f8a8ba9b0d22730d70/pyobjc_framework_coreaudio-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:78ea67483a5deb21625c189328152008d278fe1da4304da9fcc1babd12627038", size = 37012, upload-time = "2025-11-14T09:43:46.54Z" },
+ { url = "https://files.pythonhosted.org/packages/00/be/c3d660cef1ef874f42057a74931a7a05f581f6a647f5209bef96b372db86/pyobjc_framework_coreaudio-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8d81b0d0296ab4571a4ff302e5cdb52386e486eb8749e99b95b9141438558ca2", size = 38485, upload-time = "2025-11-14T09:43:49.883Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-coremedia"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/da/7d/5ad600ff7aedfef8ba8f51b11d9aaacdf247b870bd14045d6e6f232e3df9/pyobjc_framework_coremedia-12.1.tar.gz", hash = "sha256:166c66a9c01e7a70103f3ca44c571431d124b9070612ef63a1511a4e6d9d84a7", size = 89566, upload-time = "2025-11-14T10:13:49.788Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/bc/e66de468b3777d8fece69279cf6d2af51d2263e9a1ccad21b90c35c74b1b/pyobjc_framework_coremedia-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee7b822c9bb674b5b0a70bfb133410acae354e9241b6983f075395f3562f3c46", size = 29503, upload-time = "2025-11-14T09:44:54.716Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/ae/f773cdc33c34a3f9ce6db829dbf72661b65c28ea9efaec8940364185b977/pyobjc_framework_coremedia-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:161a627f5c8cd30a5ebb935189f740e21e6cd94871a9afd463efdb5d51e255fa", size = 29396, upload-time = "2025-11-14T09:44:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/ea/aee26a475b4af8ed4152d3c50b1b8955241b8e95ae789aa9ee296953bc6a/pyobjc_framework_coremedia-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:98e885b7a092083fceaef0a7fc406a01ba7bcd3318fb927e59e055931c99cac8", size = 29414, upload-time = "2025-11-14T09:45:01.336Z" },
+ { url = "https://files.pythonhosted.org/packages/db/9d/5ff10ee0ff539e125c96b8cff005457558766f942919814c968c3367cc32/pyobjc_framework_coremedia-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:d2b84149c1b3e65ec9050a3e5b617e6c0b4cdad2ab622c2d8c5747a20f013e16", size = 29477, upload-time = "2025-11-14T09:45:04.218Z" },
+ { url = "https://files.pythonhosted.org/packages/08/e2/b890658face1290c8b6b6b53a1159c822bece248f883e42302548bef38da/pyobjc_framework_coremedia-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:737ec6e0b63414f42f7188030c85975d6d2124fbf6b15b52c99b6cc20250af4d", size = 29447, upload-time = "2025-11-14T09:45:07.17Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/9e/16981d0ee04b182481ce1e497b5e0326bad6d698fe0265bb7db72b1b26b5/pyobjc_framework_coremedia-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6a9419e0d143df16a1562520a13a389417386e2a53031530af6da60c34058ced", size = 29500, upload-time = "2025-11-14T09:45:10.506Z" },
+]
+
+[[package]]
+name = "pyobjc-framework-quartz"
+version = "12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ef/dcd22b743e38b3c430fce4788176c2c5afa8bfb01085b8143b02d1e75201/pyobjc_framework_quartz-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:19f99ac49a0b15dd892e155644fe80242d741411a9ed9c119b18b7466048625a", size = 217795, upload-time = "2025-11-14T09:59:46.922Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9b/780f057e5962f690f23fdff1083a4cfda5a96d5b4d3bb49505cac4f624f2/pyobjc_framework_quartz-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7730cdce46c7e985535b5a42c31381af4aa6556e5642dc55b5e6597595e57a16", size = 218798, upload-time = "2025-11-14T10:00:01.236Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/2d/e8f495328101898c16c32ac10e7b14b08ff2c443a756a76fd1271915f097/pyobjc_framework_quartz-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:629b7971b1b43a11617f1460cd218bd308dfea247cd4ee3842eb40ca6f588860", size = 219206, upload-time = "2025-11-14T10:00:15.623Z" },
+ { url = "https://files.pythonhosted.org/packages/67/43/b1f0ad3b842ab150a7e6b7d97f6257eab6af241b4c7d14cb8e7fde9214b8/pyobjc_framework_quartz-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:53b84e880c358ba1ddcd7e8d5ea0407d760eca58b96f0d344829162cda5f37b3", size = 224317, upload-time = "2025-11-14T10:00:30.703Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/00/96249c5c7e5aaca5f688ca18b8d8ad05cd7886ebd639b3c71a6a4cadbe75/pyobjc_framework_quartz-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:42d306b07f05ae7d155984503e0fb1b701fecd31dcc5c79fe8ab9790ff7e0de0", size = 219558, upload-time = "2025-11-14T10:00:45.476Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a6/708a55f3ff7a18c403b30a29a11dccfed0410485a7548c60a4b6d4cc0676/pyobjc_framework_quartz-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0cc08fddb339b2760df60dea1057453557588908e42bdc62184b6396ce2d6e9a", size = 224580, upload-time = "2025-11-14T10:01:00.091Z" },
+]
+
+[[package]]
+name = "pyserial"
+version = "3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" },
+]
+
+[[package]]
+name = "pytest-asyncio"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
+]
+
+[[package]]
+name = "pyusb"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/6b/ce3727395e52b7b76dfcf0c665e37d223b680b9becc60710d4bc08b7b7cb/pyusb-1.3.1.tar.gz", hash = "sha256:3af070b607467c1c164f49d5b0caabe8ac78dbed9298d703a8dbf9df4052d17e", size = 77281, upload-time = "2025-01-08T23:45:01.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/b8/27e6312e86408a44fe16bd28ee12dd98608b39f7e7e57884a24e8f29b573/pyusb-1.3.1-py3-none-any.whl", hash = "sha256:bf9b754557af4717fe80c2b07cc2b923a9151f5c08d17bdb5345dac09d6a0430", size = 58465, upload-time = "2025-01-08T23:45:00.029Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
+]
+
+[[package]]
+name = "reachy-mini"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "asgiref" },
+ { name = "cv2-enumerate-cameras" },
+ { name = "eclipse-zenoh" },
+ { name = "fastapi" },
+ { name = "huggingface-hub" },
+ { name = "jinja2" },
+ { name = "libusb-package" },
+ { name = "log-throttling" },
+ { name = "numpy" },
+ { name = "opencv-python" },
+ { name = "pip" },
+ { name = "psutil" },
+ { name = "pyserial" },
+ { name = "pyusb" },
+ { name = "reachy-mini-motor-controller" },
+ { name = "reachy-mini-rust-kinematics" },
+ { name = "scipy" },
+ { name = "sounddevice" },
+ { name = "soundfile" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3f/6e/0ced70f87d1482a1e10d3b1cd0d72154013890e14c8b51a41fed696b78f1/reachy_mini-1.1.1.tar.gz", hash = "sha256:dfcac20e09869284bffc5e3243e4a3556eb9efd2ca1485ce752daabe4c5f74dc", size = 24573874, upload-time = "2025-11-25T18:03:18.583Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/41/4640e247a92f10d4e727ae50d158e44b277a7ac56cf53742fc08aec14b61/reachy_mini-1.1.1-py3-none-any.whl", hash = "sha256:9371fa52686c548817659a60170139520789be177315680465b4b6122f27fd61", size = 24632472, upload-time = "2025-11-25T18:03:15.914Z" },
+]
+
+[[package]]
+name = "reachy-mini-motor-controller"
+version = "1.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4a/8d/353b13636ebffae848c0d0b8c4bfc15c4673b059f62f9c310ee98986b0b7/reachy_mini_motor_controller-1.4.2.tar.gz", hash = "sha256:bd6e9f5c13394bc57478a09e76e01c28854ae0c80a9335a3a1b78dc80e0ee150", size = 28248, upload-time = "2025-11-25T17:56:55.263Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/20/ac97ac49969c02e0384888c73cccf0b50a7bba48b18842a96b082a8c8e89/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f405d5f948abd26e423f156fc0d165b69ec964d3a4cbfa7d2d952313cc0ff972", size = 586684, upload-time = "2025-11-25T17:56:24.378Z" },
+ { url = "https://files.pythonhosted.org/packages/68/21/796b9fd0616600b93d5b74ce2b4eb0f525f2773aae8635a8c0506a4145d7/reachy_mini_motor_controller-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:835e1d7e9383615b2684fb97cf18065f80498bb7d7d9ba980f8fe295cda68109", size = 572449, upload-time = "2025-11-25T17:56:17.627Z" },
+ { url = "https://files.pythonhosted.org/packages/07/bd/661f6a44bd436c069d38ba0fdc1c1524178c4d039dcd4326fb1918bccf91/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8403bb4b6c23c088493967c8445e5cd1ec4784f5c2ae1dab289fb9ba456055b", size = 642034, upload-time = "2025-11-25T17:55:56.725Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/82/12b52b5eeee59387866db9370e43894f66dda8feedc15be38a6963be3e9a/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1cf6d2a6dc10b5f8acc4c61a89e2e201eca84c6be741becbf9775084d29fc6e", size = 647692, upload-time = "2025-11-25T17:56:02.499Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/a0/4ad6ca320e303de61135219c3b9953b1ec1f7375181dbaaa98a43b65ecae/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a366aa59f9bde4f80f6c03757e431a3cea80f0b18992401169b484cf8928d14", size = 696265, upload-time = "2025-11-25T17:56:07.722Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/98/14cb24c497c295f91b11144bf639304116630de6862ba3095fe3a18dc3ff/reachy_mini_motor_controller-1.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df475384deadcea1c8b430839addecb84126002d54649283cce5e6465d51b543", size = 660553, upload-time = "2025-11-25T17:56:12.631Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/cb/9bc5c91075d5e551deb2d93fae89062bb94b5edd5f658b66c97c11c460d8/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e09e99bb325405f414013ce7d13f7ed36af5b9278dce2ee6bda9ea2affe6baf", size = 825331, upload-time = "2025-11-25T17:56:30.458Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/fd404636cb980604ed82dea5e7ec9c4de78a76324d36ff9efc55bb7549a7/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2f35c0b54ed396b8d9eb98dfe6dcc6d3b059433c2181030591dc84483531a25b", size = 911925, upload-time = "2025-11-25T17:56:36.802Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/1c/6d48c4c3c5c9705e20265db04f040f42d1955cb0ded817e8915684999386/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:af23d02daff28470be0e775ce63666bea06ad24ce948649751f7122996d5363d", size = 854410, upload-time = "2025-11-25T17:56:43.296Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b8/55955b7b47c8ed65bda69a221cd4805bad3d07c3c95b7002e67deec43dff/reachy_mini_motor_controller-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:610d74c3cfc28854137d00d206a6b4c4942a371376611781a244376fe3175277", size = 815823, upload-time = "2025-11-25T17:56:50.274Z" },
+ { url = "https://files.pythonhosted.org/packages/12/7c/20672ab40d1d25cdddf49cb969bfa36043d9b760e4e82275a32853846192/reachy_mini_motor_controller-1.4.2-cp311-cp311-win32.whl", hash = "sha256:abe319839f78cc3e4e8ed0ced0bc519f97bfc1e6ff6b320dc8f84b772123237d", size = 377969, upload-time = "2025-11-25T17:57:02.781Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/5e/0725df6e698fe9a83eff0f86d61c5e62878c864ce521c1a1c12645409bc5/reachy_mini_motor_controller-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:d8f7ea29ca78ee86206989c30066e17b213f4cc8975f0a275f092588cd2debc8", size = 401027, upload-time = "2025-11-25T17:56:57.17Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/6a/af2786310d5ccf84a48ff0153c7be2346ef3730ca540b20d998020c36cb8/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a41cc1bdc08b2fbba681804c6143319a6b4ef2d30093910f9776db01c5ba9595", size = 583808, upload-time = "2025-11-25T17:56:25.635Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/d3/6354fd9b34b89d2b5a639c56ae05f393d2ae7cef187bfcce60a71a40202d/reachy_mini_motor_controller-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af3cdd5197f57108044af81166dd755214f469c368e79221521b9a698da45223", size = 570332, upload-time = "2025-11-25T17:56:18.828Z" },
+ { url = "https://files.pythonhosted.org/packages/da/eb/d30bc7517a2f7c860b95978af73beede33819e717978d5560f4b5b8a2953/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed2537a512098664d73a7575af763acd5446fc2325f37cf3b9b84d37368b5f7f", size = 641541, upload-time = "2025-11-25T17:55:58.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/63/8f9c091d0b210ebc6aa7762fa09155456a088df987d73dc39cda4fb9c241/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dd7392b9fef86cca8c12d5861d8f17e2eb7e1d69b49c65fbf54e1410f9567", size = 648054, upload-time = "2025-11-25T17:56:03.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/15/a1bebc7acfcb02c12ba32e00dc1f9346fdf2468e441f983daf86ee4c7df3/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3f6ddd91e0228b3396f2c027f3e55eac0d3c4f280968ec73f2bdac37cdeda0", size = 696773, upload-time = "2025-11-25T17:56:08.895Z" },
+ { url = "https://files.pythonhosted.org/packages/06/9f/a3ab6942383198e4c105075582c2355350ef33f90ef4f877105ada181e88/reachy_mini_motor_controller-1.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0b357e49773bcd338e24ed17c4a93fb1890c97513c4cd07eec8c4f3ba06bc48", size = 660676, upload-time = "2025-11-25T17:56:13.77Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/09/7da09c39d2722d6323ed719de95f2f7777699b0049b81d25aaff9b0cc2db/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:dd05b4bbf2aa30674b2955edf8fefbdb90b522077b3c549719afd4a1d2dd325b", size = 824841, upload-time = "2025-11-25T17:56:31.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/68/2c4e8160a3d8e0e20a61ad491dccb9d17febbaa434da83e38060a7517fc9/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:377d47674a0429a01f509d9fe9b52365a715ca132bfdf4956a3b5bb219723941", size = 912195, upload-time = "2025-11-25T17:56:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/14/5f/a547e59efd62518afbb616c9e3bc2f057662fdd209f75b218ef1917bcd7b/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:742c1e077669c198a4261acf3213789ed679846a25714e74e9774984862bbc7a", size = 855751, upload-time = "2025-11-25T17:56:44.595Z" },
+ { url = "https://files.pythonhosted.org/packages/76/58/8720be3e52e302052c6d10e3c0937271e8c376d6e5a10b2dac480c94c7f2/reachy_mini_motor_controller-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9b2eb49965f2dba93a1d251b0beb4b22b81f5b18946d53ff8761097011ffaa43", size = 815778, upload-time = "2025-11-25T17:56:51.74Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/52/7af3228652ce3503243cbce68d736af00b11afd07f0f96effa6faaca677d/reachy_mini_motor_controller-1.4.2-cp312-cp312-win32.whl", hash = "sha256:e59bd6a1b89b94da835b1fbe3f483470ea0471fdb1b92d0d4bbb93a24ab7a1aa", size = 377018, upload-time = "2025-11-25T17:57:03.815Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/7a/090705f6e62ec10107ea20e78fd8b8f26f6421b9db49f7bf5e275084aa02/reachy_mini_motor_controller-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:ae99c65942130ce04a01d50dae359694a42fa91ede864ead0defd9a693b30b05", size = 399902, upload-time = "2025-11-25T17:56:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ac/fc016600786fbcdc8d048f7e42c946aa1a488e382e08b83e39934ee5f381/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:43064e6c97be9f6fb2494ebf56dda18daa0366cd70b32ee548818c908d8b3564", size = 583599, upload-time = "2025-11-25T17:56:27.042Z" },
+ { url = "https://files.pythonhosted.org/packages/07/c7/c79c33cf46bbe6301fef5520445649ce10a85a30754fce975df41f7b80b7/reachy_mini_motor_controller-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ebe3f763c011ecd3dc4bd017dfc886ecaf455d9866cab1ce8b81116d22eb6a3d", size = 570309, upload-time = "2025-11-25T17:56:20.391Z" },
+ { url = "https://files.pythonhosted.org/packages/78/e0/d301987769c1504549c06cf0ce8e132df48bde0f47d28a8768c25b7ed8ae/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c86146d3631dd73efb77d591f8a33c288b9ad394d3a6805c0685f3c5ec05266f", size = 641472, upload-time = "2025-11-25T17:55:59.645Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/52/7884ee1c04b480f8e34b4c97697a0977d314a22f6766fd5e246dfbddb404/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36052e08d331ae768f4b562e950eaa767ae4ee8a91e9334498c1bcbafe841514", size = 648510, upload-time = "2025-11-25T17:56:05.286Z" },
+ { url = "https://files.pythonhosted.org/packages/03/56/619bb25bb31f96d6172d1bfd5bbf516b5985b595f6e1c7c82481fff95c55/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b2ac6712f7a5493bb3be276fa53bb9b9e4770f13783f26ff0a18e7fc6f800d4", size = 697241, upload-time = "2025-11-25T17:56:09.99Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/b2/c82a31cf252ff6fb4d4c59a208d8375159cc796326812b7e946eea5c00a4/reachy_mini_motor_controller-1.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fcb404dc2021175d4339fb3a8f9334cd999bf8a0369e0dacb02c430edf46a0b", size = 660207, upload-time = "2025-11-25T17:56:15.23Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6e/b2d9aecc4144cffcee14b023da20b8850a11cd35375561a7e60be7132e10/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f482f61eecdeb72a7722399d0d549872a239023c263968f9e2ffac9ee69687dc", size = 824941, upload-time = "2025-11-25T17:56:33.129Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/68/72b7d8a405fe7b1785b2caaaf917958dc6691015f6c15089bfee8b35f65d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:88e1a5c1d4afe4e695d589715f0628536fe7399ec6d2751025dd785f4282a62e", size = 912805, upload-time = "2025-11-25T17:56:39.585Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/62/4ce77e08ea5dc428835202723671026f52d46e20ee2642af24ecd2c64858/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2d5c9e2645d7c6b29a8a46b4ecef72540bb2c0f983349cfcaf7c681386e7a002", size = 856256, upload-time = "2025-11-25T17:56:45.894Z" },
+ { url = "https://files.pythonhosted.org/packages/71/a6/c031586c9dd8fc735b427c5fcdd20d9360c02a6c61ab3cbb59589ad15d9d/reachy_mini_motor_controller-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffb5331150c0cdfac9910d341e82d788bc173e473e893e3f8e54938ede1e3523", size = 815565, upload-time = "2025-11-25T17:56:52.837Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/e3/5316b60491ecf3384b1b51db49317bc175561411edcc83fe1398b68736f4/reachy_mini_motor_controller-1.4.2-cp313-cp313-win32.whl", hash = "sha256:6edcf88f5d4c1bf3948328a6f65cd4518fda8cefbe55fb884fda088f363c2b60", size = 377394, upload-time = "2025-11-25T17:57:05.059Z" },
+ { url = "https://files.pythonhosted.org/packages/19/c7/41f4efca4abaf31bff5fa891ae0f594623539afd936abea51e0e546a6700/reachy_mini_motor_controller-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:602cf3294f54ceae4fa84d006aa0e97dcfcd99f7ee556cbbcf3546ac42fe8fb3", size = 399788, upload-time = "2025-11-25T17:56:59.383Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/0b/3b118d1ed20da22c3844c88247a78f6fdee679fff149d0e1ee64fc5d8aa8/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:aedcbebb270c3e6ad8898aac321f82a3d3060898ee33d75ae181b0f1df278f93", size = 581082, upload-time = "2025-11-25T17:56:28.189Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e7/c24da5194f16714d6f2a67fefaba817e2a22f68f58269500fa4495d6a29e/reachy_mini_motor_controller-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4d8d7626e3655a96266cc88d9ae1327c8b42512b61e8acaeb782d8d3352125c2", size = 567918, upload-time = "2025-11-25T17:56:21.842Z" },
+ { url = "https://files.pythonhosted.org/packages/89/69/cc9a51e04c1523213a8f4121bb1c454293d287f8c0fc4998e2640d053169/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8b27d6769f12a4bf75b8b6cb0e0dc20d154bc1422ff9e046f530434050552b86", size = 823910, upload-time = "2025-11-25T17:56:34.482Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/76/1f283234ec0fb1b349a24930485a9c584d94f064eb4d3d2e32e5607ec64e/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:07118c8cafed873541fb716928a3d4f807ac6ac7f769d7307faa4cd9bead6e6d", size = 910812, upload-time = "2025-11-25T17:56:40.804Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/22/771e3e02872c319f999c5e0830787c926d2b74b976c71f041d5429c4f785/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d552fdf890c3040232b2db783fc03ffcee230b633208e62b7a4265fa5d076598", size = 856433, upload-time = "2025-11-25T17:56:47.095Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/a3/c897ed4e7a43d205d43a985669dfbbc3943d1df8a101038c5384f34b9617/reachy_mini_motor_controller-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b250a7763a72d6864ef4cdcec088d56e4d17947a10493f7347f2001502f05ed", size = 816617, upload-time = "2025-11-25T17:56:54.099Z" },
+ { url = "https://files.pythonhosted.org/packages/03/67/63dc13fe0fad1bafe12122b10479b2e22bb4562e58ed45e14f18b3f076c5/reachy_mini_motor_controller-1.4.2-cp314-cp314-win32.whl", hash = "sha256:d9a6be4852cb8cbec2e19d2037234dce39defce653f3af9d4a46cd56d624c617", size = 376862, upload-time = "2025-11-25T17:57:06.232Z" },
+ { url = "https://files.pythonhosted.org/packages/32/08/0c4b6fc4a761ad8bd362f571d3693c94aa0c15121e122f1c01e3f38bc873/reachy_mini_motor_controller-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:aa7b6ca1b6db09e6249fdb5a31376974e82928292950cd7df4480cbd5707a425", size = 400104, upload-time = "2025-11-25T17:57:00.616Z" },
+]
+
+[[package]]
+name = "reachy-mini-rust-kinematics"
+version = "1.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/82/485aa2f28973eee6198b2af0db9e76314fd909076f06782228fa686f0e3d/reachy_mini_rust_kinematics-1.0.2.tar.gz", hash = "sha256:ca14f9193c73a53612a32cd0bfb9abecb13343bc94fe5feb95f54736b7f3cd64", size = 17187, upload-time = "2025-10-22T08:35:50.104Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2e/fd/c3801f0579da40147c7cadc1226bc3f5b7413cc590b13f53822674a910f7/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9fe481d3ae29415e9be3358493bd6d26add4b494650384c650181595da10204", size = 303080, upload-time = "2025-10-22T08:34:54.167Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/1b28508a05fde9bf80277222991273721462afa84a7a0ede6227692b6a75/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:58c87c6cb62a020f325caf8344f003ba4ebb783e20f417c6d89e3b63a72bc8a1", size = 278691, upload-time = "2025-10-22T08:34:44.295Z" },
+ { url = "https://files.pythonhosted.org/packages/de/22/9b4c4b007085db8fb329656146742414e64051d9deb8e108020fbb3aafa8/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af2f330760b5519dcf556c728ddaa889fbc1b5ef6d76a4f3ef2eee1ddd032cb4", size = 312649, upload-time = "2025-10-22T08:34:02.52Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/e7/b50b441d287e72864e32b1049ab6d740158a8330deba5ec2cfa62d5125e0/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8bffa448b0a75a570c5b8625344de3a755bca7f3a7450b045df153cef0cb402", size = 321102, upload-time = "2025-10-22T08:34:14.669Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/4b/cddf709a7012450d3255d7ab6ee6a1216e761664657ffe68806712d95c42/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ae0811ccc4132f9b034771af061c513bcb0bd18a73dad1d16abc6bad366caa", size = 337443, upload-time = "2025-10-22T08:34:35.544Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6d/908fca70c2dee3e066831fcde011fb0d8c563418c75e052208bc538105cb/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5777d235930be71186adf948388580317943a022aa3761d6f78bcffe7dfc5ee4", size = 351864, upload-time = "2025-10-22T08:34:26.064Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/e8/ddbfbc71cc2a7246de06cd4a0e934c9613a3a814406afbf8dfcd071813bf/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cbf226d07d34a6a395635b90ebb4d6e083136f07ca2e746d4431c44b00fd2d5", size = 493208, upload-time = "2025-10-22T08:35:01.445Z" },
+ { url = "https://files.pythonhosted.org/packages/75/d1/51af83f5b70c2eaf5f871efdf62c7d88116af2d5309898b2de9925857556/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bab5d550e2225c9d4d6ee69b98fd13cf75b8b3df4ab9824d7759668e04a6e5bf", size = 585005, upload-time = "2025-10-22T08:35:14.756Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/a8/cef7d415d864c8fa04362485ecb1648947a194b90426d147972ce59f4263/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7fc0243f9abc3d14aadaaa19b86d4930bf1e3bace0bfbbde73048ed80928b0f5", size = 526789, upload-time = "2025-10-22T08:35:27.367Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/4e/98e8e72f3cf0758eb4e6eb7320438dfb9e08d6ff593f6cc3661e726cf9a1/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a060b9022becd8decf9868063099fe68e598f7c07406d96795408ca862d208b3", size = 498731, upload-time = "2025-10-22T08:35:38.944Z" },
+ { url = "https://files.pythonhosted.org/packages/26/fc/d9b66b439ea27c20d01e6cc841b34b3a8cc17fb0e79acaaa8ba4f5a4c587/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win32.whl", hash = "sha256:ebf961d3ae3cd0798ed8d4e2bec8aec57f33fe2fb713852ad0ba2de4386c9c1a", size = 176607, upload-time = "2025-10-22T08:35:58.496Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/be/848776e83114ee92d9799860860be6dff036f22c9d01e85805c02a6d05fc/reachy_mini_rust_kinematics-1.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:8afa30ed7da49544c8215033d2375c2f76f3311a6a1f380fe9acd2c076f9d45f", size = 187332, upload-time = "2025-10-22T08:35:51.994Z" },
+ { url = "https://files.pythonhosted.org/packages/11/39/6c40cca1970ce47d9ae1661c87230cd37a6e2d782f34e0aec745f155d831/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:356134c5df18a01226432fbd1fb24649aced5a14da3c0406ebe2d1b3b8c002d2", size = 299587, upload-time = "2025-10-22T08:34:55.208Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/30/f9ba417bc1782d6a256ef9024eb0329c50cb7a3d12e83afd4b67047a01d6/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a232408fb7c58d6da73c40a40a120bae4f51bf9119de108530572723a00280d7", size = 276656, upload-time = "2025-10-22T08:34:46.904Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e7/e3bb2597d86f2037a1e4537f2352d7e92ff4699ee725b43a90400f15894e/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e7c3d2c73f89bf817d8c3abb8f0b23a6efdfce330a749c6e29e1e0cf6db9d7d", size = 312798, upload-time = "2025-10-22T08:34:04.219Z" },
+ { url = "https://files.pythonhosted.org/packages/62/2f/64d2c4b434ef413c948935a0f57b4a502cc7effad68dd97263cdee22df64/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba0faba18be71b68026c70ccf4db6d9ea588774053d22487f228b490204a8a00", size = 321023, upload-time = "2025-10-22T08:34:15.628Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/0e/f4cbddd5c0ed10cdba7f4baffc68f2e8102eb1d55b309690a52af3b067f8/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bed2ec420ea354ce62572afe8672ae8701a139781d9d8da97932597907019ee", size = 337756, upload-time = "2025-10-22T08:34:36.66Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/d5/ac043e65949ba904b7ac901d74b8514ab47203de3af44e96c44dd2da7a94/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a79f6517728463294d5ca4945b18d843dd16cc410815ce039b35c1198bdc355d", size = 351251, upload-time = "2025-10-22T08:34:27.06Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ef/80bb31cee07d2336013808ce3b76dee29ae04274f053e1694e3a285da555/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5404cf14cc8894962dbbc82594af453588539b4b9458b8f95f43c29bc8bd2e34", size = 493292, upload-time = "2025-10-22T08:35:02.56Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/e2/4f6c5a0d2a77111a9806243160b1aa3531ad3024a59f05c7f6157917bcc5/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2e1f9d9ce4222d7ecda5cb77c96fed763de3ac94993e60c73adf9749d26e16df", size = 584893, upload-time = "2025-10-22T08:35:15.981Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/b6/b0b57c9c46b77aa3c03f4071fca0c92809e23f9b22df5f85446ec5329fe2/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7c29c3d7b70d0576c740969cfa3e35ba98cc8e378c0867a79f53d5fee3568f77", size = 525948, upload-time = "2025-10-22T08:35:28.451Z" },
+ { url = "https://files.pythonhosted.org/packages/06/12/f4d94e8c0b1b19b47072c2041f31fb83701cff1a199d0fcf012c0489f32f/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b94ca8e94058efcd89553144b0a0b0f7c4abfe67a4cd60e2993cd0833c9e130", size = 499239, upload-time = "2025-10-22T08:35:40.026Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b6/907870cb3f588a6da58b64357c03241a28dac8a9ec9f42e91c291cb95c51/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win32.whl", hash = "sha256:edcbcedaa4aa99684588bd897733a33ff8f8d9eed8ae39bd7d0fd691874f0457", size = 176379, upload-time = "2025-10-22T08:35:59.841Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/c0/a5c1db0bf8dc55cf6b41a11463285ef440a2efa74d8b75be8443234c1acf/reachy_mini_rust_kinematics-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:eb5d6d27ce08e8816496b6049979cf64ffbf3901dd5bb290efe439f5455526c2", size = 187469, upload-time = "2025-10-22T08:35:53.307Z" },
+ { url = "https://files.pythonhosted.org/packages/13/5c/fffb02be21f666cf880042804c703de2410f32a44b0412d6df20bb156d15/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2aae2b9424a111935e26977fe2ab25c130c766e8049d72073e31a7ad78c84fb5", size = 299868, upload-time = "2025-10-22T08:34:56.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/33/b7e610c6325ed4fe439bd0be3e96850b5a5b6dc360116741bdf4c3a57ce9/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38ba383417c472ac67945a57c62ba621128b4486bd9d818512929454f47ce76d", size = 276655, upload-time = "2025-10-22T08:34:47.905Z" },
+ { url = "https://files.pythonhosted.org/packages/93/bf/a1f579e4a6930b2155c99f80ac5b96d256c0047537c0802c34f0adfcde81/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3788d9c5b5315eff026486c3367e97ea23cd4ccd3eb6c7c1e37e0d00ec6b7128", size = 312962, upload-time = "2025-10-22T08:34:05.504Z" },
+ { url = "https://files.pythonhosted.org/packages/89/cf/bf2a450adb2e6fbc5a9baa3ad9bece4d7272341723f4e76eff710463b484/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6983b555c1af6fd2c1ca8b32ae374c2b060239bb04b9b562806771ddafe81abf", size = 320871, upload-time = "2025-10-22T08:34:16.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/ad/1eab50e3c56a65ac2f14a4890ae5370984586f52adf13cd8a7963ebf423c/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca28a8cda1ab4973259ed9aa06c7173ac5899492a3140075c58225a0ed96be52", size = 337653, upload-time = "2025-10-22T08:34:37.679Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/ce/8d3802dde54f9595daa9bd7b12a3de42ba4b4b48d92d8abe7a43919d74a8/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c8591616ef354d4f0724affef58eb14374f4a06b649b7947e94b97ec22d2fb9", size = 351553, upload-time = "2025-10-22T08:34:28.126Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8c/5e1de72d044440afb8cb7480af0c9ddcdcd9654b0ac62c11113fedc731d4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45f4d9f159714e60eef22c9c00232beb34a8d27a89acce7d3ffcec2b86992b06", size = 493556, upload-time = "2025-10-22T08:35:03.963Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8b/1a33d934b9720586dc261349b500fb6a969ff1d3ba7c6f9bb7ba87d61d37/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:02a525696238dc85e9a1f3260d7d0db25074bb54f1cc2a0a4052849344026806", size = 585019, upload-time = "2025-10-22T08:35:17.344Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/5e/8cf4011456e3262f2a67b66ee77838cc56655cdcd7941efd44cb6ff8eb85/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:342b4a9adb9714f98d6569121c053800ae0a5b3904a9863ca65603bb3b9e7f0a", size = 526143, upload-time = "2025-10-22T08:35:29.636Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/3e/0d1168324c288693c07abd0050b83f682cf12a27c850e42d2601d3da84ef/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b596910a1ba0aaf79fc525ec5a8405dcd41c28946667faa549e789e5c1b93a5", size = 499284, upload-time = "2025-10-22T08:35:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/66/4e/0f69c0287a9289a0bc78d623cbf8cbd652e54ff6e854979b4985eb7fa75e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win32.whl", hash = "sha256:d5437b5d7f9093e879859d83da6888738f281fe728fad0a9f1678fe01035b4d3", size = 176631, upload-time = "2025-10-22T08:36:00.974Z" },
+ { url = "https://files.pythonhosted.org/packages/94/00/eff3fa6480da3c2b486b8bcec55ea650b4955fb874664e9e7dce205a9885/reachy_mini_rust_kinematics-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:c3c813012eacc7e779c81388f3dcb30811bee461672f9f9dc03835c73e3a5bc4", size = 187334, upload-time = "2025-10-22T08:35:54.464Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/e2/2c19fbf08a5e3836147ee345b9d75fbe1cd26df8d1f9e83b9cefa4bc80ae/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e1f563b31cbb0e0ee9f015801ef2eba31b9c37c306e3ca7737097a482355e0a", size = 312043, upload-time = "2025-10-22T08:34:06.505Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d4/873c4317a598e9709dd2aa68f0a072568b1f2db3616a2ffa76259618889e/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:874b986c44a94e1e1752f8f0b159342b0f60d4610e4df9c4bdf421e2749d949e", size = 320190, upload-time = "2025-10-22T08:34:17.889Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/11/64941229b39bfb1c39ea2ccb5fd02998915f87e40e277e4b0b744a2c3a2b/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f5060865aba96d3ffe1ac5cb93dc6439154a78ef8fdbb8a4313e4dc33064efb5", size = 492437, upload-time = "2025-10-22T08:35:05.014Z" },
+ { url = "https://files.pythonhosted.org/packages/20/47/82704725e0d5cae887a4604e9bbf7688e6a676768ab47202b018ef9f7f32/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f7ddeef8610a936afbfd7dda1713be18d207032e18604e46963729996c3d1a41", size = 583980, upload-time = "2025-10-22T08:35:18.517Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6d/bfcdc6912531c62885450646f629aa581fb116ee5806dba00c01712b02f4/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2f498398c933e87a63a5747a2c813df09dcb68ded3ba79f86d092801ced36da8", size = 525181, upload-time = "2025-10-22T08:35:30.819Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/6d/4f32bc65f83307a6eba5b62231e276bde8509d26dd8ba1a9997fb6c4b924/reachy_mini_rust_kinematics-1.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6464868b209ec5cc0e50829b25bdad8cf765a48c943a5a7f504a3ec4ed7aedcb", size = 498743, upload-time = "2025-10-22T08:35:42.183Z" },
+ { url = "https://files.pythonhosted.org/packages/85/26/2af509eb72c556a5f8628454122f61bcd55ae760ed74c2a1d8cfe21ebafe/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:419cba0b2ca01b52aba79004681201e1ad16d5a06a2ae9f4f74c45ea82e88f11", size = 276979, upload-time = "2025-10-22T08:34:49.023Z" },
+ { url = "https://files.pythonhosted.org/packages/82/62/5fdf253c4293447fe2b95329891a46292fed02fd15f71a7eafbcca842194/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c654f6e06da8c6f8f5f2613c7a8d1841dca730ba2397125418f1b26bfeb46330", size = 337650, upload-time = "2025-10-22T08:34:38.745Z" },
+ { url = "https://files.pythonhosted.org/packages/01/2e/eee9bed34de89569f52965fdbf6bd16c914c1b87f4f233e49ffa1f3f0590/reachy_mini_rust_kinematics-1.0.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:58a36591b27a80c0836ab08ff21bf605413213f079baeef6fd1e3d6b47d8a341", size = 352082, upload-time = "2025-10-22T08:34:29.451Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/2d/c4492e19625f81348c812e5e17e97e777c521fe7200006bc41ebc904dd14/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305793e3097db1a93eabeeb5bcf7a581dedb859740d5f5164d7f2b695ce90953", size = 312932, upload-time = "2025-10-22T08:34:11.265Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/b2/d6cdc7960650c609b3e902734dcd10d6eb44373ba4f0aadaefde401244ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dda63846d7196737bad42d9de847b3c4702e68e40fc0a691fed50a87b9ef0898", size = 321183, upload-time = "2025-10-22T08:34:22.468Z" },
+ { url = "https://files.pythonhosted.org/packages/51/74/d92143a61237d3f83ba9f6398f7d546cdefd8d43f820677bcf4d7ed3a265/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b307111f6ff46a1e8dc5b0d600c836ccf237106cf0bb9b87d85e079199fb733", size = 337577, upload-time = "2025-10-22T08:34:42.092Z" },
+ { url = "https://files.pythonhosted.org/packages/05/e4/5ab1e64bd201e388f3fc0bb1ee89ef166479c8482d582ee58d603ba1b6ba/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64649a8ca74f3f528d57adf02f3b5ad6eaab4e5ab40bf837554ad878f0f4ea64", size = 351547, upload-time = "2025-10-22T08:34:33.109Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/19/f754c4cac653d3133145694abb6bb2d0da15b2459c1361b35528b3d16287/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6f0eda0e6cb8d86e80937a78b44e3db5e338039480fc077503aca3c23af008e6", size = 493554, upload-time = "2025-10-22T08:35:10.535Z" },
+ { url = "https://files.pythonhosted.org/packages/af/81/14e1848dc1fb662a9dd8108bb3be4161a241055d38bac65eb9ee1fe5926c/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:12b9c538544352ee829338b3ae34623e3903294cd09ebb286f9467cad9882360", size = 585142, upload-time = "2025-10-22T08:35:23.413Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/47/a74608d37b58a162a3204ad4a1b7981ca79dc598b6c76533922cdc2bcb48/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:56eb81ad90e2b04506b4adf809b48a449fbd3972e2b32bd49038b745a6ff2d38", size = 526427, upload-time = "2025-10-22T08:35:35.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/8e/c48f621c8d954849deb081a7a8840c57e38385dad999aaf5b82e10a09f84/reachy_mini_rust_kinematics-1.0.2-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2af9062ffb5970b3318a85eaa7ce78042e7b023e7c096e34002da5aa083920dd", size = 499551, upload-time = "2025-10-22T08:35:47.616Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "rmscript"
+version = "0.1.0"
+source = { editable = "." }
+dependencies = [
+ { name = "numpy" },
+ { name = "reachy-mini" },
+]
+
+[package.optional-dependencies]
+dev = [
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "ruff" },
+ { name = "scipy" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "numpy", specifier = ">=1.24.0" },
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" },
+ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" },
+ { name = "reachy-mini", specifier = ">=1.0.0" },
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" },
+ { name = "scipy", marker = "extra == 'dev'", specifier = ">=1.10.0" },
+]
+provides-extras = ["dev"]
+
+[[package]]
+name = "ruff"
+version = "0.14.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" },
+ { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" },
+ { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" },
+ { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" },
+ { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" },
+ { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" },
+ { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" },
+ { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" },
+ { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.16.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" },
+ { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" },
+ { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" },
+ { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" },
+ { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" },
+ { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" },
+ { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" },
+ { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" },
+ { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" },
+ { url = "https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c", size = 36617856, upload-time = "2025-10-28T17:33:31.375Z" },
+ { url = "https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d", size = 28874306, upload-time = "2025-10-28T17:33:36.516Z" },
+ { url = "https://files.pythonhosted.org/packages/15/65/3a9400efd0228a176e6ec3454b1fa998fbbb5a8defa1672c3f65706987db/scipy-1.16.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9", size = 20865371, upload-time = "2025-10-28T17:33:42.094Z" },
+ { url = "https://files.pythonhosted.org/packages/33/d7/eda09adf009a9fb81827194d4dd02d2e4bc752cef16737cc4ef065234031/scipy-1.16.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4", size = 23524877, upload-time = "2025-10-28T17:33:48.483Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/6b/3f911e1ebc364cb81320223a3422aab7d26c9c7973109a9cd0f27c64c6c0/scipy-1.16.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959", size = 33342103, upload-time = "2025-10-28T17:33:56.495Z" },
+ { url = "https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88", size = 35697297, upload-time = "2025-10-28T17:34:04.722Z" },
+ { url = "https://files.pythonhosted.org/packages/04/e1/6496dadbc80d8d896ff72511ecfe2316b50313bfc3ebf07a3f580f08bd8c/scipy-1.16.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234", size = 36021756, upload-time = "2025-10-28T17:34:13.482Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/bd/a8c7799e0136b987bda3e1b23d155bcb31aec68a4a472554df5f0937eef7/scipy-1.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d", size = 38696566, upload-time = "2025-10-28T17:34:22.384Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304", size = 38529877, upload-time = "2025-10-28T17:35:51.076Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/14/9d9fbcaa1260a94f4bb5b64ba9213ceb5d03cd88841fe9fd1ffd47a45b73/scipy-1.16.3-cp313-cp313-win_arm64.whl", hash = "sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2", size = 25455366, upload-time = "2025-10-28T17:35:59.014Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a3/9ec205bd49f42d45d77f1730dbad9ccf146244c1647605cf834b3a8c4f36/scipy-1.16.3-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b", size = 37027931, upload-time = "2025-10-28T17:34:31.451Z" },
+ { url = "https://files.pythonhosted.org/packages/25/06/ca9fd1f3a4589cbd825b1447e5db3a8ebb969c1eaf22c8579bd286f51b6d/scipy-1.16.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079", size = 29400081, upload-time = "2025-10-28T17:34:39.087Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/56/933e68210d92657d93fb0e381683bc0e53a965048d7358ff5fbf9e6a1b17/scipy-1.16.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a", size = 21391244, upload-time = "2025-10-28T17:34:45.234Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/7e/779845db03dc1418e215726329674b40576879b91814568757ff0014ad65/scipy-1.16.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119", size = 23929753, upload-time = "2025-10-28T17:34:51.793Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/4b/f756cf8161d5365dcdef9e5f460ab226c068211030a175d2fc7f3f41ca64/scipy-1.16.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c", size = 33496912, upload-time = "2025-10-28T17:34:59.8Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b5/222b1e49a58668f23839ca1542a6322bb095ab8d6590d4f71723869a6c2c/scipy-1.16.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e", size = 35802371, upload-time = "2025-10-28T17:35:08.173Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/8d/5964ef68bb31829bde27611f8c9deeac13764589fe74a75390242b64ca44/scipy-1.16.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135", size = 36190477, upload-time = "2025-10-28T17:35:16.7Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/f2/b31d75cb9b5fa4dd39a0a931ee9b33e7f6f36f23be5ef560bf72e0f92f32/scipy-1.16.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6", size = 38796678, upload-time = "2025-10-28T17:35:26.354Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/1e/b3723d8ff64ab548c38d87055483714fefe6ee20e0189b62352b5e015bb1/scipy-1.16.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc", size = 38640178, upload-time = "2025-10-28T17:35:35.304Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/f3/d854ff38789aca9b0cc23008d607ced9de4f7ab14fa1ca4329f86b3758ca/scipy-1.16.3-cp313-cp313t-win_arm64.whl", hash = "sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a", size = 25803246, upload-time = "2025-10-28T17:35:42.155Z" },
+ { url = "https://files.pythonhosted.org/packages/99/f6/99b10fd70f2d864c1e29a28bbcaa0c6340f9d8518396542d9ea3b4aaae15/scipy-1.16.3-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6", size = 36606469, upload-time = "2025-10-28T17:36:08.741Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/74/043b54f2319f48ea940dd025779fa28ee360e6b95acb7cd188fad4391c6b/scipy-1.16.3-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657", size = 28872043, upload-time = "2025-10-28T17:36:16.599Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/e1/24b7e50cc1c4ee6ffbcb1f27fe9f4c8b40e7911675f6d2d20955f41c6348/scipy-1.16.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26", size = 20862952, upload-time = "2025-10-28T17:36:22.966Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3a/3e8c01a4d742b730df368e063787c6808597ccb38636ed821d10b39ca51b/scipy-1.16.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc", size = 23508512, upload-time = "2025-10-28T17:36:29.731Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/60/c45a12b98ad591536bfe5330cb3cfe1850d7570259303563b1721564d458/scipy-1.16.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22", size = 33413639, upload-time = "2025-10-28T17:36:37.982Z" },
+ { url = "https://files.pythonhosted.org/packages/71/bc/35957d88645476307e4839712642896689df442f3e53b0fa016ecf8a3357/scipy-1.16.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc", size = 35704729, upload-time = "2025-10-28T17:36:46.547Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/15/89105e659041b1ca11c386e9995aefacd513a78493656e57789f9d9eab61/scipy-1.16.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0", size = 36086251, upload-time = "2025-10-28T17:36:55.161Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/87/c0ea673ac9c6cc50b3da2196d860273bc7389aa69b64efa8493bdd25b093/scipy-1.16.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800", size = 38716681, upload-time = "2025-10-28T17:37:04.1Z" },
+ { url = "https://files.pythonhosted.org/packages/91/06/837893227b043fb9b0d13e4bd7586982d8136cb249ffb3492930dab905b8/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d", size = 39358423, upload-time = "2025-10-28T17:38:20.005Z" },
+ { url = "https://files.pythonhosted.org/packages/95/03/28bce0355e4d34a7c034727505a02d19548549e190bedd13a721e35380b7/scipy-1.16.3-cp314-cp314-win_arm64.whl", hash = "sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f", size = 26135027, upload-time = "2025-10-28T17:38:24.966Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6f/69f1e2b682efe9de8fe9f91040f0cd32f13cfccba690512ba4c582b0bc29/scipy-1.16.3-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c", size = 37028379, upload-time = "2025-10-28T17:37:14.061Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/2d/e826f31624a5ebbab1cd93d30fd74349914753076ed0593e1d56a98c4fb4/scipy-1.16.3-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40", size = 29400052, upload-time = "2025-10-28T17:37:21.709Z" },
+ { url = "https://files.pythonhosted.org/packages/69/27/d24feb80155f41fd1f156bf144e7e049b4e2b9dd06261a242905e3bc7a03/scipy-1.16.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d", size = 21391183, upload-time = "2025-10-28T17:37:29.559Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d3/1b229e433074c5738a24277eca520a2319aac7465eea7310ea6ae0e98ae2/scipy-1.16.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa", size = 23930174, upload-time = "2025-10-28T17:37:36.306Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/d9e148b0ec680c0f042581a2be79a28a7ab66c0c4946697f9e7553ead337/scipy-1.16.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8", size = 33497852, upload-time = "2025-10-28T17:37:42.228Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/22/4e5f7561e4f98b7bea63cf3fd7934bff1e3182e9f1626b089a679914d5c8/scipy-1.16.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353", size = 35798595, upload-time = "2025-10-28T17:37:48.102Z" },
+ { url = "https://files.pythonhosted.org/packages/83/42/6644d714c179429fc7196857866f219fef25238319b650bb32dde7bf7a48/scipy-1.16.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146", size = 36186269, upload-time = "2025-10-28T17:37:53.72Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/70/64b4d7ca92f9cf2e6fc6aaa2eecf80bb9b6b985043a9583f32f8177ea122/scipy-1.16.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d", size = 38802779, upload-time = "2025-10-28T17:37:59.393Z" },
+ { url = "https://files.pythonhosted.org/packages/61/82/8d0e39f62764cce5ffd5284131e109f07cf8955aef9ab8ed4e3aa5e30539/scipy-1.16.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7", size = 39471128, upload-time = "2025-10-28T17:38:05.259Z" },
+ { url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
+]
+
+[[package]]
+name = "sounddevice"
+version = "0.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload-time = "2024-10-12T09:40:12.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload-time = "2024-10-12T09:40:05.605Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload-time = "2024-10-12T09:40:07.436Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload-time = "2024-10-12T09:40:09.355Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload-time = "2024-10-12T09:40:11.065Z" },
+]
+
+[[package]]
+name = "soundfile"
+version = "0.13.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e1/41/9b873a8c055582859b239be17902a85339bec6a30ad162f98c9b0288a2cc/soundfile-0.13.1.tar.gz", hash = "sha256:b2c68dab1e30297317080a5b43df57e302584c49e2942defdde0acccc53f0e5b", size = 46156, upload-time = "2025-01-25T09:17:04.831Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/28/e2a36573ccbcf3d57c00626a21fe51989380636e821b341d36ccca0c1c3a/soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445", size = 25751, upload-time = "2025-01-25T09:16:44.235Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/ab/73e97a5b3cc46bba7ff8650a1504348fa1863a6f9d57d7001c6b67c5f20e/soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33", size = 1142250, upload-time = "2025-01-25T09:16:47.583Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/e5/58fd1a8d7b26fc113af244f966ee3aecf03cb9293cb935daaddc1e455e18/soundfile-0.13.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:743f12c12c4054921e15736c6be09ac26b3b3d603aef6fd69f9dde68748f2593", size = 1101406, upload-time = "2025-01-25T09:16:49.662Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ae/c0e4a53d77cf6e9a04179535766b3321b0b9ced5f70522e4caf9329f0046/soundfile-0.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9c9e855f5a4d06ce4213f31918653ab7de0c5a8d8107cd2427e44b42df547deb", size = 1235729, upload-time = "2025-01-25T09:16:53.018Z" },
+ { url = "https://files.pythonhosted.org/packages/57/5e/70bdd9579b35003a489fc850b5047beeda26328053ebadc1fb60f320f7db/soundfile-0.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:03267c4e493315294834a0870f31dbb3b28a95561b80b134f0bd3cf2d5f0e618", size = 1313646, upload-time = "2025-01-25T09:16:54.872Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/df/8c11dc4dfceda14e3003bb81a0d0edcaaf0796dd7b4f826ea3e532146bba/soundfile-0.13.1-py2.py3-none-win32.whl", hash = "sha256:c734564fab7c5ddf8e9be5bf70bab68042cd17e9c214c06e365e20d64f9a69d5", size = 899881, upload-time = "2025-01-25T09:16:56.663Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e9/6b761de83277f2f02ded7e7ea6f07828ec78e4b229b80e4ca55dd205b9dc/soundfile-0.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:1e70a05a0626524a69e9f0f4dd2ec174b4e9567f4d8b6c11d38b5c289be36ee9", size = 1019162, upload-time = "2025-01-25T09:16:59.573Z" },
+]
+
+[[package]]
+name = "starlette"
+version = "0.50.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.38.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.22.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" },
+ { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" },
+ { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" },
+ { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" },
+ { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" },
+ { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" },
+ { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" },
+ { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" },
+ { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" },
+ { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" },
+ { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" },
+ { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" },
+ { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" },
+ { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" },
+ { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" },
+ { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" },
+ { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" },
+ { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" },
+ { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" },
+ { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" },
+ { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" },
+ { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" },
+ { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" },
+ { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" },
+ { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" },
+ { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" },
+ { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" },
+ { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" },
+ { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" },
+ { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" },
+ { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" },
+ { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" },
+ { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" },
+ { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" },
+ { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" },
+ { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" },
+ { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" },
+ { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" },
+ { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" },
+ { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" },
+ { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
+]
+
+[[package]]
+name = "yarl"
+version = "1.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" },
+ { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" },
+ { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" },
+ { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" },
+ { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" },
+ { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" },
+ { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" },
+ { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" },
+ { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" },
+ { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" },
+ { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" },
+ { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" },
+ { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" },
+ { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" },
+ { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" },
+ { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" },
+ { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" },
+ { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" },
+ { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" },
+ { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" },
+ { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" },
+ { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" },
+ { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" },
+ { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" },
+ { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" },
+ { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" },
+ { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" },
+ { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" },
+ { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" },
+ { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" },
+ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
+]