From cff305da36d0098349faaaee27ff8c89710604b6 Mon Sep 17 00:00:00 2001 From: AnonJon Date: Fri, 10 Jul 2026 19:17:17 -0500 Subject: [PATCH] openapi update --- .github/workflows/ci.yml | 41 +- .gitignore | 4 + CONTRIBUTING.md | 56 +- README.md | 109 +- docs/durability.md | 115 + examples/authoring_system.py | 52 + examples/watch_tasks.py | 2 +- openapi/PIN.json | 7 + openapi/build_openapi.py | 1715 ++++ openapi/openapi.yaml | 7885 +++++++++++++++++++ openapi/schemas/a2a.yaml | 216 + openapi/schemas/agent-policy.yaml | 53 + openapi/schemas/agent-role.yaml | 33 + openapi/schemas/agent-system.yaml | 134 + openapi/schemas/agent.yaml | 78 + openapi/schemas/common.yaml | 313 + openapi/schemas/context-adapter.yaml | 33 + openapi/schemas/eval-dataset.yaml | 63 + openapi/schemas/eval-run.yaml | 111 + openapi/schemas/mcp-server.yaml | 81 + openapi/schemas/memory.yaml | 39 + openapi/schemas/model-endpoint.yaml | 47 + openapi/schemas/sealed-secret.yaml | 66 + openapi/schemas/secret.yaml | 41 + openapi/schemas/task-approval.yaml | 49 + openapi/schemas/task-schedule.yaml | 45 + openapi/schemas/task-webhook.yaml | 68 + openapi/schemas/task.yaml | 196 + openapi/schemas/tool-approval.yaml | 40 + openapi/schemas/tool-permission.yaml | 47 + openapi/schemas/tool.yaml | 120 + openapi/schemas/worker.yaml | 41 + pyproject.toml | 10 +- scripts/bundle_openapi.py | 83 + scripts/check_schema_parity.py | 154 + scripts/generate_models.py | 116 + scripts/sync_openapi.py | 67 + src/orloj_sdk/__init__.py | 4 + src/orloj_sdk/_tasks.py | 98 + src/orloj_sdk/authoring.py | 233 + src/orloj_sdk/client.py | 22 +- src/orloj_sdk/models/__init__.py | 142 + src/orloj_sdk/models/_generated/__init__.py | 2 + src/orloj_sdk/models/_generated/models.py | 1723 ++++ src/orloj_sdk/models/a2a.py | 110 + src/orloj_sdk/models/agent.py | 4 + src/orloj_sdk/models/agent_policy.py | 3 + src/orloj_sdk/models/agent_system.py | 34 + src/orloj_sdk/models/context_adapter.py | 28 + src/orloj_sdk/models/eval.py | 157 + src/orloj_sdk/models/mcp_server.py | 9 +- src/orloj_sdk/models/memory.py | 1 + src/orloj_sdk/models/model_endpoint.py | 1 + src/orloj_sdk/models/observability.py | 84 + src/orloj_sdk/models/sealed_secret.py | 46 + src/orloj_sdk/models/task.py | 30 + src/orloj_sdk/models/task_approval.py | 45 + src/orloj_sdk/models/task_schedule.py | 2 + src/orloj_sdk/models/task_webhook.py | 11 + src/orloj_sdk/models/tool.py | 25 + src/orloj_sdk/models/tool_approval.py | 1 + src/orloj_sdk/resources/a2a.py | 220 + src/orloj_sdk/resources/auth.py | 19 + src/orloj_sdk/resources/context_adapters.py | 16 + src/orloj_sdk/resources/eval_datasets.py | 16 + src/orloj_sdk/resources/eval_runs.py | 225 + src/orloj_sdk/resources/sealed_secrets.py | 30 + src/orloj_sdk/resources/task_approvals.py | 152 + src/orloj_sdk/resources/tasks.py | 338 +- src/orloj_sdk/resources/tool_approvals.py | 8 +- tests/test_authoring.py | 69 + tests/test_models/test_parity_fields.py | 101 + tests/test_resources/test_urls.py | 210 + tests/test_tasks_run.py | 126 + uv.lock | 1153 +++ 75 files changed, 17780 insertions(+), 48 deletions(-) create mode 100644 docs/durability.md create mode 100644 examples/authoring_system.py create mode 100644 openapi/PIN.json create mode 100644 openapi/build_openapi.py create mode 100644 openapi/openapi.yaml create mode 100644 openapi/schemas/a2a.yaml create mode 100644 openapi/schemas/agent-policy.yaml create mode 100644 openapi/schemas/agent-role.yaml create mode 100644 openapi/schemas/agent-system.yaml create mode 100644 openapi/schemas/agent.yaml create mode 100644 openapi/schemas/common.yaml create mode 100644 openapi/schemas/context-adapter.yaml create mode 100644 openapi/schemas/eval-dataset.yaml create mode 100644 openapi/schemas/eval-run.yaml create mode 100644 openapi/schemas/mcp-server.yaml create mode 100644 openapi/schemas/memory.yaml create mode 100644 openapi/schemas/model-endpoint.yaml create mode 100644 openapi/schemas/sealed-secret.yaml create mode 100644 openapi/schemas/secret.yaml create mode 100644 openapi/schemas/task-approval.yaml create mode 100644 openapi/schemas/task-schedule.yaml create mode 100644 openapi/schemas/task-webhook.yaml create mode 100644 openapi/schemas/task.yaml create mode 100644 openapi/schemas/tool-approval.yaml create mode 100644 openapi/schemas/tool-permission.yaml create mode 100644 openapi/schemas/tool.yaml create mode 100644 openapi/schemas/worker.yaml create mode 100644 scripts/bundle_openapi.py create mode 100644 scripts/check_schema_parity.py create mode 100644 scripts/generate_models.py create mode 100644 scripts/sync_openapi.py create mode 100644 src/orloj_sdk/_tasks.py create mode 100644 src/orloj_sdk/authoring.py create mode 100644 src/orloj_sdk/models/_generated/__init__.py create mode 100644 src/orloj_sdk/models/_generated/models.py create mode 100644 src/orloj_sdk/models/a2a.py create mode 100644 src/orloj_sdk/models/context_adapter.py create mode 100644 src/orloj_sdk/models/eval.py create mode 100644 src/orloj_sdk/models/observability.py create mode 100644 src/orloj_sdk/models/sealed_secret.py create mode 100644 src/orloj_sdk/models/task_approval.py create mode 100644 src/orloj_sdk/resources/a2a.py create mode 100644 src/orloj_sdk/resources/context_adapters.py create mode 100644 src/orloj_sdk/resources/eval_datasets.py create mode 100644 src/orloj_sdk/resources/eval_runs.py create mode 100644 src/orloj_sdk/resources/sealed_secrets.py create mode 100644 src/orloj_sdk/resources/task_approvals.py create mode 100644 tests/test_authoring.py create mode 100644 tests/test_models/test_parity_fields.py create mode 100644 tests/test_tasks_run.py create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2789869..7c2cd63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,27 +20,37 @@ jobs: with: fetch-depth: 0 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v8.3.2 with: python-version: ${{ matrix.python-version }} + enable-cache: true - name: Install dependencies + run: uv sync --frozen + + - name: OpenAPI schema parity (hand-written models vs vendored schemas) + run: uv run python scripts/check_schema_parity.py + + - name: Regenerate OpenAPI models (freshness gate) run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" + uv run python scripts/generate_models.py + git diff --exit-code -- src/orloj_sdk/models/_generated || { + echo "::error::Generated models are stale. Run 'uv run python scripts/generate_models.py' and commit." + exit 1 + } - name: Ruff check - run: ruff check src/orloj_sdk tests examples + run: uv run ruff check src/orloj_sdk tests examples - name: Ruff format - run: ruff format --check src/orloj_sdk tests examples + run: uv run ruff format --check src/orloj_sdk tests examples - name: Mypy - run: mypy src/orloj_sdk + run: uv run mypy src/orloj_sdk - name: Pytest - run: pytest --cov=orloj_sdk --cov-report=xml --cov-report=term-missing -q + run: uv run pytest --cov=orloj_sdk --cov-report=xml --cov-report=term-missing -q publish: needs: test @@ -51,15 +61,18 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v8.3.2 with: python-version: "3.12" - - run: pip install build twine - - run: python -m build - - run: twine upload dist/* + + - name: Build + run: uv build + + - name: Publish to PyPI + run: uv publish env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} github-release: needs: publish diff --git a/.gitignore b/.gitignore index b5674d0..3a2eddc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ ENV/ env/ .env/ .virtualenv/ +.uv-tools/ # PyInstaller *.manifest @@ -92,3 +93,6 @@ plan.md # Profiling .prof + +# OpenAPI bundle is regenerated by scripts/generate_models.py +openapi/openapi.bundle.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eadb201..1b85533 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,26 +4,60 @@ Thanks for helping improve the Orloj Python SDK. This document describes how to ## Prerequisites -- **Python 3.10+** (CI runs 3.10 through 3.13) -- A virtual environment is recommended +- **[uv](https://docs.astral.sh/uv/)** (install: `curl -LsSf https://astral.sh/uv/install.sh | sh`) +- **Python 3.10+** (CI runs 3.10 through 3.13; `uv` can install a matching interpreter) ## Local setup ```bash -python -m venv .venv -source .venv/bin/activate # Windows: .venv\Scripts\activate -pip install -e ".[dev]" +uv sync ``` +This creates `.venv`, installs the package in editable mode, and installs the `dev` dependency group from `pyproject.toml` (locked in `uv.lock`). + +Use `uv run …` for tools (or `source .venv/bin/activate` if you prefer an activated shell). + +## Keeping OpenAPI / models in sync + +The wire contract is vendored under [`openapi/`](openapi/) and pinned in [`openapi/PIN.json`](openapi/PIN.json) +(commit/tag of [OrlojHQ/orloj](https://github.com/OrlojHQ/orloj)). + +1. **Sync** the OpenAPI tree from a local orloj checkout: + + ```bash + uv run python scripts/sync_openapi.py --orloj-root /path/to/orloj + ``` + +2. **Regenerate** reference models (CI freshness gate): + + ```bash + uv run python scripts/generate_models.py + ``` + +3. **Check** hand-written `orloj_sdk.models` still cover every OpenAPI schema property: + + ```bash + uv run python scripts/check_schema_parity.py + ``` + +4. If parity fails, update the corresponding file under `src/orloj_sdk/models/` (and resources if new routes appeared), then re-run the checks. + +Public imports stay on the hand-written models (`OrlojModel`, aliases, DX). +`src/orloj_sdk/models/_generated/` is machine-owned — do not edit it. + +API-visible changes in orloj must update OpenAPI there first; then sync/regenerate/parity here in the same SDK PR when possible. + ## Checks (run before opening a PR) These mirror [`.github/workflows/ci.yml`](.github/workflows/ci.yml): ```bash -ruff check src/orloj_sdk tests examples -ruff format src/orloj_sdk tests examples # or: ruff format --check ... to verify only -mypy src/orloj_sdk -pytest --cov=orloj_sdk --cov-report=term-missing +uv run python scripts/check_schema_parity.py +uv run python scripts/generate_models.py && git diff --exit-code -- src/orloj_sdk/models/_generated +uv run ruff check src/orloj_sdk tests examples +uv run ruff format src/orloj_sdk tests examples # or: uv run ruff format --check ... to verify only +uv run mypy src/orloj_sdk +uv run pytest --cov=orloj_sdk --cov-report=term-missing ``` Fix any failures locally; CI must pass on the matrix of Python versions. @@ -35,13 +69,15 @@ Fix any failures locally; CI must pass on the matrix of Python versions. ## Release version -The published package version is taken from **Git tags** (`hatch-vcs` / setuptools-scm), not from editing `src/orloj_sdk/_version.py`. At release time, create an annotated tag whose name matches the version (for example `v0.2.0` for `0.2.0`) and push it; CI builds and uploads that version to PyPI, then creates a **GitHub Release** for that tag (with auto-generated notes) so it appears under the repo’s Releases page. Runtime `orloj_sdk.__version__` comes from the installed package metadata (after `pip install -e .` or installing from PyPI). +The published package version is taken from **Git tags** (`hatch-vcs` / setuptools-scm), not from editing `src/orloj_sdk/_version.py`. At release time, create an annotated tag whose name matches the version (for example `v0.2.0` for `0.2.0`) and push it; CI builds and uploads that version to PyPI, then creates a **GitHub Release** for that tag (with auto-generated notes) so it appears under the repo’s Releases page. Runtime `orloj_sdk.__version__` comes from the installed package metadata (after `uv sync` or installing from PyPI). ## Pull requests - Keep changes focused on one concern when possible. - Update or add tests when behavior changes. - Match existing style: typing, Pydantic usage, and public API patterns in `src/orloj_sdk`. +- If the change depends on a newer Orloj API, sync `openapi/` and regenerate `_generated` in the same PR. +- If you change dependencies in `pyproject.toml`, run `uv lock` and commit the updated `uv.lock`. ## License diff --git a/README.md b/README.md index 43659c5..b92758b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # orloj — Official Python SDK -Typed Python client for the [Orloj](https://orloj.dev) v1 REST API: synchronous and asynchronous usage (`httpx`), Pydantic v2 models, SSE watch streams, and cursor-based pagination. +Typed Python client for the [Orloj](https://orloj.dev) v1 REST API: synchronous and asynchronous usage (`httpx`), Pydantic v2 models, SSE watch streams, cursor-based pagination, `tasks.run(wait=True)` with typed output, and a small graph-as-code authoring layer. ## Requirements @@ -8,18 +8,18 @@ Typed Python client for the [Orloj](https://orloj.dev) v1 REST API: synchronous ## Install -From [PyPI](https://pypi.org/project/orloj-sdk/): +From [PyPI](https://pypi.org/project/orloj-sdk/) (with [uv](https://docs.astral.sh/uv/)): ```bash -pip install orloj-sdk +uv add orloj-sdk ``` -Install with `pip install orloj-sdk`, then import the `orloj_sdk` package (`from orloj_sdk import OrlojClient`). +Then import the `orloj_sdk` package (`from orloj_sdk import OrlojClient`). `pip install orloj-sdk` works too. From a git checkout: ```bash -pip install -e ".[dev]" +uv sync ``` ## Configuration @@ -31,7 +31,7 @@ The client reads defaults from the environment (same order as `orlojctl`): | API token | `api_token=` | `ORLOJCTL_API_TOKEN`, `ORLOJ_API_TOKEN` | | Base URL | `base_url=` | `ORLOJCTL_SERVER`, `ORLOJ_SERVER` | -If unset, the base URL defaults to `http://127.0.0.1:8080` and the namespace defaults to `default`. +If unset, the base URL defaults to `http://127.0.0.1:8080` and the namespace defaults to `default`. HTTP `max_retries` defaults to `3` for production-friendly transient failure handling. ## Quickstart @@ -46,6 +46,45 @@ for agent in client.agents.list_all(): print(agent.metadata.name) ``` +### Run a task and wait + +```python +from pydantic import BaseModel +from orloj_sdk import OrlojClient + +class Answer(BaseModel): + summary: str + +client = OrlojClient(api_token="your-token") +answer = client.tasks.run( + name="job-1", + system="demo-pipeline", + input={"query": "hello"}, + wait=True, + output_type=Answer, + output_key="result", +) +print(answer.summary) +``` + +### Author a system in Python + +```python +from orloj_sdk import OrlojClient +from orloj_sdk.authoring import agent, edge, node, system + +@agent("researcher", model_ref="default-model") +def researcher() -> None: + """Research the query and list key facts.""" + +pipeline = ( + system("demo-pipeline") + .add_agent(researcher) + .set_graph({"researcher": node()}) +) +pipeline.apply(OrlojClient(api_token="your-token")) +``` + ### Async ```python @@ -55,6 +94,49 @@ async with AsyncOrlojClient(api_token="your-token") as client: page = await client.agents.list(limit=20) ``` +## Durability + +Orloj durability (leases, retries, idempotency, dead-letter) lives in the **server**. See [docs/durability.md](docs/durability.md) for guarantees, terminal phases, production server baseline, and SDK wait defaults. Temporal interop is intentionally deferred — Orloj remains the control-plane identity. + +## OpenAPI sync + +The SDK vendors Orloj’s OpenAPI under [`openapi/`](openapi/) (see [`openapi/PIN.json`](openapi/PIN.json)). CI fails if: + +- hand-written models lag schema properties (`scripts/check_schema_parity.py`), or +- generated models under `src/orloj_sdk/models/_generated/` are stale (`scripts/generate_models.py`). + +Workflow: sync from orloj → regenerate → fix parity → commit. Details in [CONTRIBUTING.md](CONTRIBUTING.md#keeping-openapi--models-in-sync). + +## API surface + +Resource accessors on `OrlojClient` / `AsyncOrlojClient`: + +| Attribute | Path prefix | +| --------- | ----------- | +| `agents` | `/v1/agents` | +| `agent_systems` | `/v1/agent-systems` | +| `model_endpoints` | `/v1/model-endpoints` | +| `tools` | `/v1/tools` | +| `secrets` | `/v1/secrets` | +| `sealed_secrets` | `/v1/sealed-secrets` (+ `get_public_key`) | +| `memories` | `/v1/memories` | +| `context_adapters` | `/v1/context-adapters` | +| `eval_datasets` | `/v1/eval-datasets` | +| `eval_runs` | `/v1/eval-runs` (start/cancel/export/finalize/annotate/compare) | +| `agent_policies` | `/v1/agent-policies` | +| `agent_roles` | `/v1/agent-roles` | +| `tool_permissions` | `/v1/tool-permissions` | +| `tool_approvals` | `/v1/tool-approvals` | +| `task_approvals` | `/v1/task-approvals` | +| `tasks` | `/v1/tasks` (`run`/`wait`/`output`, logs/messages/metrics/cancel/retry/watch) | +| `task_schedules` | `/v1/task-schedules` | +| `task_webhooks` | `/v1/task-webhooks` | +| `workers` | `/v1/workers` | +| `mcp_servers` | `/v1/mcp-servers` | +| `auth` | auth/tokens/health/capabilities (`cli_token`, …) | +| `events` | `/v1/events/watch` | +| `a2a` | A2A registry, agent cards, JSON-RPC invoke | + ## Examples Runnable scripts (require a running Orloj API): @@ -64,11 +146,12 @@ Runnable scripts (require a running Orloj API): | `examples/quickstart.py` | `whoami` and list agents | | `examples/watch_tasks.py` | SSE watch on a task until completion | | `examples/multi_agent_system.py` | Create an `AgentSystem` with a graph | +| `examples/authoring_system.py` | Graph-as-code authoring + `apply` | | `examples/async_example.py` | `AsyncOrlojClient` with `asyncio.gather` | ```bash export ORLOJ_API_TOKEN=... -python examples/quickstart.py +uv run python examples/quickstart.py ``` ## Development @@ -76,13 +159,11 @@ python examples/quickstart.py See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, checks, and pull request expectations. ```bash -python -m venv .venv -source .venv/bin/activate # Windows: .venv\Scripts\activate -pip install -e ".[dev]" -ruff check src/orloj_sdk tests examples -ruff format src/orloj_sdk tests examples -mypy src/orloj_sdk -pytest --cov=orloj_sdk +uv sync +uv run ruff check src/orloj_sdk tests examples +uv run ruff format src/orloj_sdk tests examples +uv run mypy src/orloj_sdk +uv run pytest --cov=orloj_sdk ``` ## License diff --git a/docs/durability.md b/docs/durability.md new file mode 100644 index 0000000..e95e02b --- /dev/null +++ b/docs/durability.md @@ -0,0 +1,115 @@ +# Durability guarantees + +Orloj is a durable agent control plane. The Python SDK is a client for that +control plane — durability lives in the server (leases, retries, idempotency), +not in the SDK process. This page summarizes what you can rely on when building +production agents, and how the SDK defaults align with that model. + +For the full product story see the Orloj docs: +[Tasks](https://docs.orloj.dev/concepts/tasks/task), +[Execution model](https://docs.orloj.dev/concepts/execution-model), +[Production configuration](https://docs.orloj.dev/operations/configuration). + +## What Orloj guarantees + +| Guarantee | Mechanism | +| --------- | --------- | +| Lease-based ownership | A worker claims a task with a time-bounded lease and renews it via heartbeats. | +| Safe takeover | If the lease expires (crash / partition), another worker may reclaim the task. | +| Owner-only execution | Only `status.claimedBy` may process messages for that task. | +| Idempotent replay | Message idempotency keys protect crash recovery and redelivery. | +| Capped retries | Task- and message-level retry with backoff/jitter; exhaustion → `DeadLetter`. | +| Durable handoffs (prod) | Message-driven mode + NATS JetStream for agent-to-agent delivery. | + +Docs describe this as **exactly-once processing semantics** under lease ownership +plus idempotency. Treat side effects in tools as needing their own idempotency +keys — the control plane dedupes *task messages*, not arbitrary external writes. + +## Terminal task phases + +| Phase | Meaning | +| ----- | ------- | +| `Succeeded` | Graph completed successfully. | +| `Failed` | Execution failed (also what cancel maps to). May still be retryable server-side. | +| `DeadLetter` | Retries exhausted; needs investigation. | + +`WaitingApproval` is **not** terminal — the task is paused on a tool/task approval. + +`client.tasks.run(wait=True)` and `client.tasks.wait(...)` stop on those three +terminal phases. Success raises nothing; `Failed` / `DeadLetter` raise +`TaskFailedError` unless `raise_on_failure=False`. + +## Production defaults (server) + +Recommended Orloj deployment baseline (not SDK settings): + +- `--storage-backend=postgres` +- `--task-execution-mode=message-driven` +- `--agent-message-bus-backend=nats-jetstream` +- Shared `--secret-encryption-key` / `ORLOJ_SECRET_ENCRYPTION_KEY` on all processes +- Workers with `--agent-message-consume` +- Lease ~`30s`, heartbeat ~`10s` (server defaults) + +Local/dev often uses in-memory storage and sequential/embedded workers; that is +fine for demos, not for production durability. + +## SDK client defaults + +| Setting | Default | Why | +| ------- | ------- | --- | +| `max_retries` | `3` | Retry transient HTTP 429/5xx and connection blips (was `0`). | +| `tasks.run` timeout | `300s` | Matches `orlojctl run` / `wait` defaults. | +| `tasks.run` poll | `2s` | Matches CLI poll interval. | + +Override per client or per call: + +```python +from orloj_sdk import OrlojClient + +client = OrlojClient(max_retries=5) +task = client.tasks.run( + name="job-1", + system="demo-pipeline", + input={"query": "hello"}, + wait=True, + timeout=600, +) +``` + +## What the SDK does *not* provide + +- Process-local workflow durability (if your Python process dies mid-`run()`, + the **task** continues on Orloj; your waiter does not). +- Exactly-once tool side effects — use tool idempotency / external dedupe. +- Temporal (or other orchestrator) interop — planned as an enterprise + land-and-expand integration **after** the REST client, DX helpers, and + authoring layer are solid. Orloj remains the control-plane identity. + +## Waiting patterns + +```python +# Create + wait (poll) +task = client.tasks.run(name="t1", system="sys", input={"q": "x"}, wait=True) + +# Create without waiting, then wait later +created = client.tasks.run(name="t2", system="sys", wait=False) +finished = client.tasks.wait(created.metadata.name) + +# Typed output +from pydantic import BaseModel + +class Answer(BaseModel): + summary: str + +answer = client.tasks.run( + name="t3", + system="sys", + input={"q": "x"}, + wait=True, + output_type=Answer, + output_key="result", # optional: parse one JSON string field +) +``` + +Prefer SSE `client.tasks.watch(...)` when you need live phase updates rather +than poll-based waiting. diff --git a/examples/authoring_system.py b/examples/authoring_system.py new file mode 100644 index 0000000..c1f7610 --- /dev/null +++ b/examples/authoring_system.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Author an AgentSystem in Python and apply it via the REST client. + +Requires existing ModelEndpoint named in model_ref (default: adjust below). +""" + +from __future__ import annotations + +from orloj_sdk import OrlojClient +from orloj_sdk.authoring import agent, edge, node, system + + +def main() -> None: + client = OrlojClient() + + researcher = agent("researcher", model_ref="default-model", tools=[])(lambda: None).with_prompt( + "Research the user query and list key facts." + ) + writer = agent("writer", model_ref="default-model")(lambda: None).with_prompt( + "Write a short summary from the research notes." + ) + + pipeline = ( + system("demo-authored-pipeline") + .add_agent(researcher) + .add_agent(writer) + .set_graph( + { + "researcher": node(edges=[edge("writer")]), + "writer": node(), + } + ) + ) + + created = pipeline.apply(client) + print(f"created AgentSystem {created.metadata.name}") + + # Optional: run a task and wait + # from pydantic import BaseModel + # class Out(BaseModel): + # summary: str + # result = client.tasks.run( + # name="demo-run-1", + # system=created.metadata.name, + # input={"query": "What is Orloj?"}, + # wait=True, + # ) + + +if __name__ == "__main__": + main() diff --git a/examples/watch_tasks.py b/examples/watch_tasks.py index 9b4fb25..85ddcd1 100644 --- a/examples/watch_tasks.py +++ b/examples/watch_tasks.py @@ -22,7 +22,7 @@ def main() -> None: t = event.resource phase = t.status.phase if t.status else None print(f"{event.type}: {t.metadata.name} phase={phase}") - if phase in ("Completed", "Failed", "Cancelled"): + if phase in ("Succeeded", "Failed", "DeadLetter"): break diff --git a/openapi/PIN.json b/openapi/PIN.json new file mode 100644 index 0000000..c6bc982 --- /dev/null +++ b/openapi/PIN.json @@ -0,0 +1,7 @@ +{ + "repository": "https://github.com/OrlojHQ/orloj", + "commit": "4bdbae6685619de7a56ea18cb617f68b661f862c", + "tag": "v0.18.0", + "synced_at": "2026-07-09T23:53:49Z", + "note": "Vendored OpenAPI from orloj working tree (includes uncommitted path coverage: eval-datasets, eval-runs, context-adapters, agent.json alias, cli-token in generator). Update via: python scripts/sync_openapi.py --orloj-root /path/to/orloj" +} diff --git a/openapi/build_openapi.py b/openapi/build_openapi.py new file mode 100644 index 0000000..6869a0b --- /dev/null +++ b/openapi/build_openapi.py @@ -0,0 +1,1715 @@ +#!/usr/bin/env python3 +"""Regenerate openapi/openapi.yaml from structured path definitions.""" +from __future__ import annotations + +import copy +import json +import pathlib +import subprocess + + +def schema_ref(path: str) -> dict: + return {"$ref": path} + + +def json_body(ref_path: str) -> dict: + return {"application/json": {"schema": schema_ref(ref_path)}} + + +def text_plain_error() -> dict: + return { + "text/plain": { + "schema": schema_ref("./schemas/common.yaml#/components/schemas/PlainTextError") + } + } + + +SEC_READER = [{"bearerAuth": []}, {"sessionCookie": []}] +SEC_WRITER = [{"bearerAuth": []}, {"sessionCookie": []}] +SEC_ADMIN = [{"bearerAuth": []}, {"sessionCookie": []}] +SEC_CTRL = [{"bearerAuth": []}, {"sessionCookie": []}] +SEC_A2A = [{"bearerAuth": []}] + + +def list_op(tag: str, list_ref: str) -> dict: + return { + "tags": [tag], + "parameters": [ + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + { + "name": "limit", + "in": "query", + "schema": {"type": "integer", "minimum": 1, "maximum": 1000}, + }, + {"name": "after", "in": "query", "schema": {"type": "string"}}, + {"name": "labelSelector", "in": "query", "schema": {"type": "string"}}, + ], + "security": SEC_READER, + "responses": { + "200": {"description": "OK", "content": json_body(list_ref)}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + + +def list_tasks(tag: str, list_ref: str) -> dict: + op = list_op(tag, list_ref) + op["parameters"].insert( + 3, + {"name": "offset", "in": "query", "schema": { + "type": "integer", "minimum": 0}}, + ) + return op + + +def post_create(tag: str, item_ref: str) -> dict: + return { + "tags": [tag], + "security": SEC_WRITER, + "requestBody": {"required": True, "content": json_body(item_ref)}, + "responses": { + "201": {"description": "Created", "content": json_body(item_ref)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + + +def get_one(tag: str, item_ref: str) -> dict: + return { + "tags": [tag], + "parameters": [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "security": SEC_READER, + "responses": { + "200": {"description": "OK", "content": json_body(item_ref)}, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + + +def put_one(tag: str, item_ref: str) -> dict: + return { + "tags": [tag], + "parameters": [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + { + "name": "If-Match", + "in": "header", + "required": False, + "schema": {"type": "string"}, + }, + ], + "security": SEC_WRITER, + "requestBody": {"required": True, "content": json_body(item_ref)}, + "responses": { + "200": {"description": "OK", "content": json_body(item_ref)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + + +def delete_one(tag: str) -> dict: + return { + "tags": [tag], + "parameters": [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "security": SEC_WRITER, + "responses": { + "204": {"description": "Deleted"}, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + + +def status_get_put(tag: str) -> tuple[dict, dict]: + base_params = [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ] + env = "./schemas/common.yaml#/components/schemas/StatusEnvelope" + get_op = { + "tags": [tag], + "parameters": base_params, + "security": SEC_READER, + "responses": { + "200": {"description": "OK", "content": json_body(env)}, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + put_op = { + "tags": [tag], + "parameters": base_params + + [ + { + "name": "If-Match", + "in": "header", + "required": False, + "schema": {"type": "string"}, + } + ], + "security": SEC_CTRL, + "requestBody": {"required": True, "content": json_body(env)}, + "responses": { + "200": {"description": "OK", "content": json_body(env)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + return get_op, put_op + + +def sse_response(desc: str) -> dict: + return { + "200": { + "description": desc, + "content": {"text/event-stream": {"schema": {"type": "string"}}}, + }, + "default": {"description": "Error", "content": text_plain_error()}, + } + + +def watch_params() -> list: + return [ + {"name": "resourceVersion", "in": "query", "schema": {"type": "string"}}, + {"name": "name", "in": "query", "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ] + + +def add_crud( + paths: dict, + base: str, + tag: str, + list_ref: str, + item_ref: str, + *, + with_status: bool = False, +) -> None: + paths[base] = {"get": list_op(tag, list_ref), + "post": post_create(tag, item_ref)} + if with_status: + g, p = status_get_put(tag) + paths[f"{base}/{{name}}/status"] = {"get": g, "put": p} + paths[f"{base}/{{name}}"] = { + "get": get_one(tag, item_ref), + "put": put_one(tag, item_ref), + "delete": delete_one(tag), + } + + +def build() -> dict: + paths: dict = {} + + paths["/v1/agents"] = { + "get": list_op("agents", "./schemas/agent.yaml#/components/schemas/AgentList"), + "post": post_create("agents", "./schemas/agent.yaml#/components/schemas/Agent"), + } + paths["/v1/agents/watch"] = { + "get": { + "tags": ["agents"], + "security": SEC_READER, + "parameters": watch_params(), + "responses": sse_response( + "SSE; `event: resource` with WatchResourceEvent JSON in data" + ), + } + } + g, p = status_get_put("agents") + paths["/v1/agents/{name}/status"] = {"get": g, "put": p} + paths["/v1/agents/{name}/logs"] = { + "get": { + "tags": ["agents"], + "security": SEC_READER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/NamedLogsResponse" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/agents/{name}"] = { + "get": get_one("agents", "./schemas/agent.yaml#/components/schemas/Agent"), + "put": put_one("agents", "./schemas/agent.yaml#/components/schemas/Agent"), + "delete": delete_one("agents"), + } + + add_crud( + paths, + "/v1/agent-systems", + "agent-systems", + "./schemas/agent-system.yaml#/components/schemas/AgentSystemList", + "./schemas/agent-system.yaml#/components/schemas/AgentSystem", + with_status=True, + ) + add_crud( + paths, + "/v1/model-endpoints", + "model-endpoints", + "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpointList", + "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint", + with_status=True, + ) + mep = paths["/v1/model-endpoints/{name}"] + mep["put"] = { + **mep["put"], + "summary": "Replace a model endpoint (supports rename)", + "description": ( + "Updates the model endpoint identified by the path `name`. If `metadata.name` in the " + "body differs from the path,\nthe stored resource is renamed to match the body " + "(HTTP 409 if that name already exists in the namespace)." + ), + } + add_crud( + paths, + "/v1/tools", + "tools", + "./schemas/tool.yaml#/components/schemas/ToolList", + "./schemas/tool.yaml#/components/schemas/Tool", + with_status=True, + ) + add_crud( + paths, + "/v1/secrets", + "secrets", + "./schemas/secret.yaml#/components/schemas/SecretList", + "./schemas/secret.yaml#/components/schemas/Secret", + with_status=False, + ) + sec = paths["/v1/secrets/{name}"] + sec["get"] = { + **sec["get"], + "summary": "Get a secret", + "description": ( + "Returns the secret with `spec.data` values redacted as `***`; " + "`spec.stringData` is omitted (see **secrets** tag)." + ), + } + sec["get"]["responses"]["200"]["description"] = ( + "OK (sensitive values redacted in `spec.data`; `spec.stringData` omitted)" + ) + sec["put"] = { + **sec["put"], + "summary": "Replace a secret (supports rename)", + "description": ( + "See the **secrets** tag for redaction (`***`) and PUT merge behavior; " + "PUT rename follows the same rules as other namespaced resources." + ), + } + add_crud( + paths, + "/v1/sealed-secrets", + "sealed-secrets", + "./schemas/sealed-secret.yaml#/components/schemas/SealedSecretList", + "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret", + with_status=False, + ) + paths["/v1/sealing-key/public"] = { + "get": { + "tags": ["sealed-secrets"], + "summary": "Get the active sealing public key", + "description": ( + "Returns the active public key used to seal `SealedSecret` manifests. " + "When no active sealing key is configured, the server returns HTTP 503." + ), + "security": SEC_READER, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/sealed-secret.yaml#/components/schemas/SealedSecretPublicKeyResponse" + ), + }, + "503": {"description": "Sealing key unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + add_crud( + paths, + "/v1/memories", + "memories", + "./schemas/memory.yaml#/components/schemas/MemoryList", + "./schemas/memory.yaml#/components/schemas/Memory", + with_status=True, + ) + + paths["/v1/memories/{name}/entries"] = { + "get": { + "tags": ["memories"], + "security": SEC_READER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + {"name": "q", "in": "query", "schema": {"type": "string"}}, + {"name": "prefix", "in": "query", "schema": {"type": "string"}}, + {"name": "limit", "in": "query", "schema": {"type": "integer"}}, + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/MemoryEntriesResponse" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "500": {"description": "Backend error", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + add_crud( + paths, + "/v1/context-adapters", + "context-adapters", + "./schemas/context-adapter.yaml#/components/schemas/ContextAdapterList", + "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter", + with_status=False, + ) + + add_crud( + paths, + "/v1/eval-datasets", + "eval-datasets", + "./schemas/eval-dataset.yaml#/components/schemas/EvalDatasetList", + "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset", + with_status=True, + ) + + add_crud( + paths, + "/v1/eval-runs", + "eval-runs", + "./schemas/eval-run.yaml#/components/schemas/EvalRunList", + "./schemas/eval-run.yaml#/components/schemas/EvalRun", + with_status=True, + ) + eval_run_ref = "./schemas/eval-run.yaml#/components/schemas/EvalRun" + eval_result_ref = "./schemas/eval-run.yaml#/components/schemas/EvalSampleResult" + for action in ("start", "cancel", "finalize"): + paths[f"/v1/eval-runs/{{name}}/{action}"] = { + "post": { + "tags": ["eval-runs"], + "summary": f"{action.capitalize()} an eval run", + "security": SEC_WRITER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "responses": { + "200": {"description": "OK", "content": json_body(eval_run_ref)}, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/eval-runs/{name}/export"] = { + "get": { + "tags": ["eval-runs"], + "summary": "Export eval run sample results", + "security": SEC_READER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + { + "name": "format", + "in": "query", + "schema": {"type": "string", "enum": ["json", "csv"], "default": "json"}, + }, + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": schema_ref(eval_result_ref), + } + }, + "text/csv": {"schema": {"type": "string"}}, + }, + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/eval-runs/{name}/results"] = { + "post": { + "tags": ["eval-runs"], + "summary": "Bulk-import sample annotations", + "security": SEC_WRITER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sample_name": {"type": "string"}, + "score": {"type": ["number", "null"]}, + "pass": {"type": ["boolean", "null"]}, + "comment": {"type": "string"}, + }, + }, + } + } + }, + }, + "responses": { + "200": {"description": "OK", "content": json_body(eval_run_ref)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/eval-runs/{name}/results/{sample_name}"] = { + "put": { + "tags": ["eval-runs"], + "summary": "Annotate a single sample result", + "security": SEC_WRITER, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + { + "name": "sample_name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "score": {"type": ["number", "null"]}, + "pass": {"type": ["boolean", "null"]}, + "comment": {"type": "string"}, + }, + } + } + }, + }, + "responses": { + "200": {"description": "OK", "content": json_body(eval_run_ref)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/eval-runs/compare"] = { + "get": { + "tags": ["eval-runs"], + "summary": "Compare multiple eval runs", + "security": SEC_READER, + "parameters": [ + { + "name": "runs", + "in": "query", + "required": True, + "schema": {"type": "string"}, + "description": "Comma-separated eval run names (at least two).", + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/eval-run.yaml#/components/schemas/EvalRunCompareResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + add_crud( + paths, + "/v1/agent-policies", + "agent-policies", + "./schemas/agent-policy.yaml#/components/schemas/AgentPolicyList", + "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy", + with_status=True, + ) + add_crud( + paths, + "/v1/agent-roles", + "agent-roles", + "./schemas/agent-role.yaml#/components/schemas/AgentRoleList", + "./schemas/agent-role.yaml#/components/schemas/AgentRole", + with_status=False, + ) + add_crud( + paths, + "/v1/tool-permissions", + "tool-permissions", + "./schemas/tool-permission.yaml#/components/schemas/ToolPermissionList", + "./schemas/tool-permission.yaml#/components/schemas/ToolPermission", + with_status=False, + ) + + paths["/v1/tool-approvals"] = { + "get": list_op( + "tool-approvals", + "./schemas/tool-approval.yaml#/components/schemas/ToolApprovalList", + ), + "post": post_create( + "tool-approvals", + "./schemas/tool-approval.yaml#/components/schemas/ToolApproval", + ), + } + paths["/v1/tool-approvals/{name}"] = { + "get": get_one( + "tool-approvals", + "./schemas/tool-approval.yaml#/components/schemas/ToolApproval", + ), + "delete": delete_one("tool-approvals"), + "put": { + "tags": ["tool-approvals"], + "summary": "Not supported", + "parameters": [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}} + ], + "security": SEC_WRITER, + "responses": { + "405": { + "description": "Method not allowed", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + } + for suf, title in [ + ("approve", "Approve pending tool invocation"), + ("deny", "Deny pending tool invocation"), + ]: + paths[f"/v1/tool-approvals/{{name}}/{suf}"] = { + "post": { + "tags": ["tool-approvals"], + "summary": title, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", + "schema": {"type": "string"}}, + ], + "security": SEC_WRITER, + "requestBody": { + "required": False, + "content": json_body( + "./schemas/common.yaml#/components/schemas/ToolApprovalDecisionRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "503": { + "description": "Store unavailable", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/task-approvals"] = { + "get": list_op( + "task-approvals", + "./schemas/task-approval.yaml#/components/schemas/TaskApprovalList", + ), + "post": post_create( + "task-approvals", + "./schemas/task-approval.yaml#/components/schemas/TaskApproval", + ), + } + paths["/v1/task-approvals/{name}"] = { + "get": get_one( + "task-approvals", + "./schemas/task-approval.yaml#/components/schemas/TaskApproval", + ), + "delete": delete_one("task-approvals"), + "put": { + "tags": ["task-approvals"], + "summary": "Not supported", + "parameters": [ + {"name": "name", "in": "path", "required": True, + "schema": {"type": "string"}} + ], + "security": SEC_WRITER, + "responses": { + "405": { + "description": "Method not allowed", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + } + for suf, title in [ + ("approve", "Approve pending task review"), + ("deny", "Deny pending task review"), + ]: + paths[f"/v1/task-approvals/{{name}}/{suf}"] = { + "post": { + "tags": ["task-approvals"], + "summary": title, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", + "schema": {"type": "string"}}, + ], + "security": SEC_WRITER, + "requestBody": { + "required": False, + "content": json_body( + "./schemas/common.yaml#/components/schemas/TaskApprovalDecisionRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "503": { + "description": "Store unavailable", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/task-approvals/{name}/request-changes"] = { + "post": { + "tags": ["task-approvals"], + "summary": "Request changes on pending task review", + "description": ( + "Reruns the producing agent with reviewer feedback. " + "Requires `comment` or the legacy `reason` field and returns `409 Conflict` " + "when the checkpoint disables request_changes or has reached max_review_cycles." + ), + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "security": SEC_WRITER, + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/TaskApprovalRequestChangesRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "503": { + "description": "Store unavailable", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/tasks"] = { + "get": list_tasks("tasks", "./schemas/task.yaml#/components/schemas/TaskList"), + "post": post_create("tasks", "./schemas/task.yaml#/components/schemas/Task"), + } + paths["/v1/tasks/watch"] = { + "get": { + "tags": ["tasks"], + "security": SEC_READER, + "parameters": watch_params(), + "responses": sse_response("SSE resource watch for tasks"), + } + } + tg, tp = status_get_put("tasks") + paths["/v1/tasks/{name}/status"] = {"get": tg, "put": tp} + + msg_params = [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + {"name": "phase", "in": "query", "schema": {"type": "string"}}, + {"name": "lifecycle", "in": "query", "schema": {"type": "string"}}, + {"name": "from_agent", "in": "query", "schema": {"type": "string"}}, + {"name": "to_agent", "in": "query", "schema": {"type": "string"}}, + {"name": "branch_id", "in": "query", "schema": {"type": "string"}}, + {"name": "trace_id", "in": "query", "schema": {"type": "string"}}, + {"name": "limit", "in": "query", "schema": { + "type": "integer", "minimum": 0}}, + ] + + paths["/v1/tasks/{name}/logs"] = { + "get": { + "tags": ["tasks"], + "security": SEC_READER, + "parameters": msg_params[:2], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/NamedLogsResponse" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/tasks/{name}/messages"] = { + "get": { + "tags": ["tasks"], + "security": SEC_READER, + "parameters": msg_params, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/TaskMessageListResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/tasks/{name}/metrics"] = { + "get": { + "tags": ["tasks"], + "security": SEC_READER, + "parameters": msg_params, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/TaskMessageMetricsResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/tasks/{name}"] = { + "get": get_one("tasks", "./schemas/task.yaml#/components/schemas/Task"), + "put": put_one("tasks", "./schemas/task.yaml#/components/schemas/Task"), + "delete": delete_one("tasks"), + } + + add_crud( + paths, + "/v1/task-schedules", + "task-schedules", + "./schemas/task-schedule.yaml#/components/schemas/TaskScheduleList", + "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule", + with_status=True, + ) + paths["/v1/task-schedules/watch"] = { + "get": { + "tags": ["task-schedules"], + "security": SEC_READER, + "parameters": watch_params(), + "responses": sse_response("SSE resource watch for task schedules"), + } + } + + paths["/v1/task-webhooks"] = { + "get": { + "tags": ["task-webhooks"], + "security": SEC_READER, + "parameters": [ + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + {"name": "labelSelector", "in": "query", + "schema": {"type": "string"}}, + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/task-webhook.yaml#/components/schemas/TaskWebhookList" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + "post": post_create( + "task-webhooks", + "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook", + ), + } + paths["/v1/task-webhooks/watch"] = { + "get": { + "tags": ["task-webhooks"], + "security": SEC_READER, + "parameters": watch_params(), + "responses": sse_response("SSE resource watch for task webhooks"), + } + } + wg, wp = status_get_put("task-webhooks") + paths["/v1/task-webhooks/{name}/status"] = {"get": wg, "put": wp} + paths["/v1/task-webhooks/{name}"] = { + "get": get_one( + "task-webhooks", + "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook", + ), + "put": put_one( + "task-webhooks", + "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook", + ), + "delete": delete_one("task-webhooks"), + } + + add_crud( + paths, + "/v1/workers", + "workers", + "./schemas/worker.yaml#/components/schemas/WorkerList", + "./schemas/worker.yaml#/components/schemas/Worker", + with_status=True, + ) + + paths["/v1/mcp-servers"] = { + "get": list_op( + "mcp-servers", + "./schemas/mcp-server.yaml#/components/schemas/McpServerList", + ), + "post": { + **post_create( + "mcp-servers", + "./schemas/mcp-server.yaml#/components/schemas/McpServer", + ), + "security": SEC_ADMIN, + }, + } + paths["/v1/mcp-servers/{name}"] = { + "get": get_one( + "mcp-servers", + "./schemas/mcp-server.yaml#/components/schemas/McpServer", + ), + "put": { + **put_one( + "mcp-servers", + "./schemas/mcp-server.yaml#/components/schemas/McpServer", + ), + "security": SEC_ADMIN, + }, + "delete": { + "tags": ["mcp-servers"], + "security": SEC_ADMIN, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "responses": { + "200": { + "description": "Deleted", + "content": json_body( + "./schemas/common.yaml#/components/schemas/McpServerDeletedResponse" + ), + }, + "404": {"description": "Not found", "content": text_plain_error()}, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + } + + wh_body = { + "required": True, + "content": { + "application/json": { + "schema": {"type": "object", "additionalProperties": True}, + }, + "application/octet-stream": { + "schema": {"type": "string", "format": "binary"}, + }, + }, + } + paths["/v1/webhook-deliveries/{endpoint_id}"] = { + "post": { + "tags": ["task-webhooks"], + "summary": "Inbound webhook (verified per TaskWebhook auth profile generic|github|hmac|shared_token)", + "security": SEC_WRITER, + "parameters": [ + { + "name": "endpoint_id", + "in": "path", + "required": True, + "schema": {"type": "string"}, + } + ], + "requestBody": wh_body, + "responses": { + "202": { + "description": "Accepted", + "content": json_body( + "./schemas/common.yaml#/components/schemas/WebhookDeliveryResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": { + "description": "Signature verification failed", + "content": text_plain_error(), + }, + "404": {"description": "Unknown endpoint", "content": text_plain_error()}, + "409": {"description": "Suspended or conflict", "content": text_plain_error()}, + "500": {"description": "Server error", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/events/watch"] = { + "get": { + "tags": ["events"], + "security": SEC_READER, + "parameters": [ + {"name": "since", "in": "query", "schema": {"type": "string"}}, + {"name": "source", "in": "query", "schema": {"type": "string"}}, + {"name": "type", "in": "query", "schema": {"type": "string"}}, + {"name": "kind", "in": "query", "schema": {"type": "string"}}, + {"name": "name", "in": "query", "schema": {"type": "string"}}, + {"name": "namespace", "in": "query", "schema": {"type": "string"}}, + ], + "responses": { + "200": { + "description": "SSE; `event: event` with BusEvent JSON", + "content": {"text/event-stream": {"schema": {"type": "string"}}}, + }, + "503": { + "description": "Event bus unavailable", + "content": text_plain_error(), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/namespaces"] = { + "get": { + "tags": ["system"], + "security": SEC_READER, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/NamespacesResponse" + ), + }, + "503": {"description": "Store unavailable", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/capabilities"] = { + "get": { + "tags": ["system"], + "security": SEC_READER, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/CapabilitySnapshot" + ), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + # --- A2A Protocol --- + + a2a_card_ref = "./schemas/a2a.yaml#/components/schemas/AgentCard" + a2a_req_ref = "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcRequest" + a2a_resp_ref = "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcResponse" + a2a_registry_ref = "./schemas/a2a.yaml#/components/schemas/A2ARegistryResponse" + + paths["/.well-known/agent-card.json"] = { + "get": { + "tags": ["a2a"], + "summary": "Get the default Agent Card", + "description": ( + "Returns the A2A Agent Card when exactly one AgentSystem has " + "spec.a2a.enabled=true. Use the registry or per-system card URLs when " + "multiple systems are exposed." + ), + "security": [], + "responses": { + "200": {"description": "OK", "content": json_body(a2a_card_ref)}, + "404": {"description": "No single default AgentSystem available", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + # Alias registered by the server for Agent Card discovery compatibility. + paths["/.well-known/agent.json"] = copy.deepcopy(paths["/.well-known/agent-card.json"]) + system_card_get = { + "get": { + "tags": ["a2a"], + "summary": "Get Agent Card for a specific AgentSystem", + "description": ( + "Returns the A2A Agent Card for the named AgentSystem when " + "spec.a2a.enabled=true." + ), + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + ], + "security": [], + "responses": { + "200": {"description": "OK", "content": json_body(a2a_card_ref)}, + "404": {"description": "AgentSystem not found or not A2A-enabled", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/agent-systems/{name}/.well-known/agent-card.json"] = system_card_get + paths["/v1/agents/{name}/.well-known/agent-card.json"] = { + "get": { + **system_card_get["get"], + "summary": "Get Agent Card for a specific AgentSystem (legacy path)", + "description": "Legacy alias for /v1/agent-systems/{name}/.well-known/agent-card.json.", + } + } + + a2a_rpc_post = { + "tags": ["a2a"], + "summary": "A2A JSON-RPC endpoint", + "description": ( + "JSON-RPC 2.0 endpoint implementing the A2A protocol. Supports methods: " + "tasks/send, tasks/get, tasks/cancel, tasks/sendSubscribe." + ), + "security": SEC_A2A, + "requestBody": {"required": True, "content": json_body(a2a_req_ref)}, + "responses": { + "200": {"description": "JSON-RPC response", "content": json_body(a2a_resp_ref)}, + "400": {"description": "Bad request", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + paths["/a2a"] = {"post": a2a_rpc_post} + system_a2a_post = { + "post": { + **a2a_rpc_post, + "summary": "A2A JSON-RPC endpoint for a specific AgentSystem", + "description": ( + "JSON-RPC 2.0 endpoint for the named AgentSystem. The system name in the path " + "determines routing; no target field is needed in request params." + ), + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + }, + ], + } + } + paths["/v1/agent-systems/{name}/a2a"] = system_a2a_post + paths["/v1/agents/{name}/a2a"] = { + "post": { + **system_a2a_post["post"], + "summary": "A2A JSON-RPC endpoint for a specific AgentSystem (legacy path)", + "description": "Legacy alias for /v1/agent-systems/{name}/a2a.", + } + } + + paths["/v1/a2a/agents"] = { + "get": { + "tags": ["a2a"], + "summary": "List A2A agent registry", + "description": ( + "Returns locally exposed A2A-enabled AgentSystem cards visible to the " + "bearer token plus configured remote agent entries with cache metadata." + ), + "security": SEC_A2A, + "responses": { + "200": {"description": "OK", "content": json_body(a2a_registry_ref)}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/healthz"] = { + "get": { + "tags": ["system"], + "security": [], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/HealthResponse" + ), + } + }, + } + } + paths["/metrics"] = { + "get": { + "tags": ["system"], + "security": SEC_READER, + "responses": { + "200": { + "description": "Prometheus text exposition", + "content": {"text/plain": {"schema": {"type": "string"}}}, + }, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + paths["/v1/auth/config"] = { + "get": { + "tags": ["auth"], + "security": [], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthConfigResponse" + ), + }, + "500": {"description": "Server error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/setup"] = { + "post": { + "tags": ["auth"], + "security": [], + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthCredentialsRequest" + ), + }, + "responses": { + "201": { + "description": "Created session", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthMeResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "409": {"description": "Already configured", "content": text_plain_error()}, + "429": {"description": "Rate limited", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/login"] = { + "post": { + "tags": ["auth"], + "security": [], + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthCredentialsRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthMeResponse" + ), + }, + "401": {"description": "Invalid credentials", "content": text_plain_error()}, + "409": {"description": "Setup required", "content": text_plain_error()}, + "429": {"description": "Rate limited", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/cli-token"] = { + "post": { + "tags": ["auth"], + "summary": "Authenticate with username/password and obtain a CLI bearer token", + "description": ( + "Validates credentials against the native user store and mints a new API " + "bearer token for CLI use. Only available when the server runs in native " + "auth mode." + ), + "security": [], + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthCLITokenRequest" + ), + }, + "responses": { + "201": { + "description": "Token created", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthCLITokenResponse" + ), + }, + "400": { + "description": "Bad request (non-native mode or missing fields)", + "content": text_plain_error(), + }, + "401": {"description": "Invalid credentials", "content": text_plain_error()}, + "429": {"description": "Rate limited", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/logout"] = { + "post": { + "tags": ["auth"], + "security": [], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/OkStatusMessage" + ), + }, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/me"] = { + "get": { + "tags": ["auth"], + "security": [], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthMeResponse" + ), + } + }, + } + } + pwd_status = { + "type": "object", + "properties": {"status": {"type": "string"}}, + } + paths["/v1/auth/change-password"] = { + "post": { + "tags": ["auth"], + "security": SEC_READER, + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthChangePasswordRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": {"application/json": {"schema": pwd_status}}, + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "429": {"description": "Rate limited", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/admin/reset-password"] = { + "post": { + "tags": ["auth"], + "security": SEC_ADMIN, + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthResetPasswordRequest" + ), + }, + "responses": { + "200": { + "description": "OK", + "content": {"application/json": {"schema": pwd_status}}, + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "429": {"description": "Rate limited", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/auth/users"] = { + "get": { + "tags": ["auth"], + "security": SEC_ADMIN, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthUserListResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + "post": { + "tags": ["auth"], + "security": SEC_ADMIN, + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthUserCreateRequest" + ), + }, + "responses": { + "201": { + "description": "Created", + "content": json_body( + "./schemas/common.yaml#/components/schemas/AuthUserCreateResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + } + paths["/v1/auth/users/{username}"] = { + "delete": { + "tags": ["auth"], + "security": SEC_ADMIN, + "parameters": [ + { + "name": "username", + "in": "path", + "required": True, + "schema": {"type": "string"}, + } + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/OkStatusMessage" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + paths["/v1/tokens"] = { + "get": { + "tags": ["auth"], + "security": SEC_ADMIN, + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/TokenListResponse" + ), + }, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + "post": { + "tags": ["auth"], + "security": SEC_ADMIN, + "requestBody": { + "required": True, + "content": json_body( + "./schemas/common.yaml#/components/schemas/TokenCreateRequest" + ), + }, + "responses": { + "201": { + "description": "Created", + "content": json_body( + "./schemas/common.yaml#/components/schemas/TokenCreateResponse" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "409": {"description": "Conflict", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + }, + } + paths["/v1/tokens/{name}"] = { + "delete": { + "tags": ["auth"], + "security": SEC_ADMIN, + "parameters": [ + { + "name": "name", + "in": "path", + "required": True, + "schema": {"type": "string"}, + } + ], + "responses": { + "200": { + "description": "OK", + "content": json_body( + "./schemas/common.yaml#/components/schemas/OkStatusMessage" + ), + }, + "400": {"description": "Bad request", "content": text_plain_error()}, + "401": {"description": "Unauthorized", "content": text_plain_error()}, + "403": {"description": "Forbidden", "content": text_plain_error()}, + "404": {"description": "Not found", "content": text_plain_error()}, + "default": {"description": "Error", "content": text_plain_error()}, + }, + } + } + + return { + "openapi": "3.1.0", + "servers": [ + { + "url": "/", + "description": "Server root (set host/port when calling the API)", + } + ], + "info": { + "title": "Orloj API", + "version": "1.0.0", + "license": {"name": "Apache-2.0", "identifier": "Apache-2.0"}, + "description": ( + "Control plane HTTP API for Orloj (v1).\n\n" + "Most error responses use `text/plain` bodies. A future release may " + "standardize errors as JSON for clients and tooling.\n\n" + "Authentication: bearer token (`Authorization: Bearer ...`) and/or " + "session cookie (`orloj_session`, or `__Host-orloj_session` over HTTPS). " + "Effective requirements depend on server configuration " + "(`ORLOJ_API_TOKEN` / `ORLOJ_API_TOKENS`, native auth mode, etc.).\n\n" + "Namespaced PUT behavior: for supported replacement-style " + "`/v1//{name}` endpoints, request-body `metadata.name` " + "is authoritative. When it differs from the path, the server " + "renames the stored resource within the same namespace " + "(HTTP 409 if the destination name already exists)." + ), + }, + "tags": [ + {"name": "agents"}, + {"name": "agent-systems"}, + {"name": "model-endpoints"}, + {"name": "tools"}, + { + "name": "secrets", + "description": ( + "GET responses redact `spec.data` entry values as the literal string `***` and " + "omit `spec.stringData`. PUT merges `***` placeholders for existing keys " + "(from `spec.data`, and from `spec.stringData` when present) so clients can " + "rename or edit metadata without re-entering unchanged secrets; new keys still " + "require real base64 (`spec.data`) or plaintext (`spec.stringData`). " + "PUT may rename the secret when `metadata.name` differs from the path (HTTP 409 if the target name exists)." + ), + }, + {"name": "sealed-secrets"}, + {"name": "memories"}, + {"name": "context-adapters"}, + {"name": "eval-datasets"}, + {"name": "eval-runs"}, + {"name": "agent-policies"}, + {"name": "agent-roles"}, + {"name": "tool-permissions"}, + {"name": "tool-approvals"}, + {"name": "task-approvals"}, + {"name": "tasks"}, + {"name": "task-schedules"}, + {"name": "task-webhooks"}, + {"name": "workers"}, + {"name": "mcp-servers"}, + {"name": "a2a", "description": ( + "Agent-to-Agent (A2A) protocol endpoints. Provides Agent Card discovery, " + "JSON-RPC task operations, and a registry of local and remote A2A agents." + )}, + {"name": "auth"}, + {"name": "system"}, + {"name": "events"}, + ], + "paths": paths, + "components": { + "securitySchemes": { + "bearerAuth": {"type": "http", "scheme": "bearer"}, + "sessionCookie": { + "type": "apiKey", + "in": "cookie", + "name": "orloj_session", + "description": "Session cookie; `__Host-orloj_session` is used for secure sites.", + }, + } + }, + } + + +def main() -> None: + root = pathlib.Path(__file__).resolve().parent + doc = build() + tmp = root / "openapi.tmp.json" + tmp.write_text(json.dumps(doc, indent=2)) + yaml_path = root / "openapi.yaml" + subprocess.run( + [ + "ruby", + "-rjson", + "-ryaml", + "-e", + "File.write(ARGV[1], YAML.dump(JSON.parse(File.read(ARGV[0]))))", + str(tmp), + str(yaml_path), + ], + check=True, + ) + tmp.unlink() + print(f"Wrote {yaml_path}") + + +if __name__ == "__main__": + main() diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml new file mode 100644 index 0000000..ef01730 --- /dev/null +++ b/openapi/openapi.yaml @@ -0,0 +1,7885 @@ +--- +openapi: 3.1.0 +servers: +- url: "/" + description: Server root (set host/port when calling the API) +info: + title: Orloj API + version: 1.0.0 + license: + name: Apache-2.0 + identifier: Apache-2.0 + description: |- + Control plane HTTP API for Orloj (v1). + + Most error responses use `text/plain` bodies. A future release may standardize errors as JSON for clients and tooling. + + Authentication: bearer token (`Authorization: Bearer ...`) and/or session cookie (`orloj_session`, or `__Host-orloj_session` over HTTPS). Effective requirements depend on server configuration (`ORLOJ_API_TOKEN` / `ORLOJ_API_TOKENS`, native auth mode, etc.). + + Namespaced PUT behavior: for supported replacement-style `/v1//{name}` endpoints, request-body `metadata.name` is authoritative. When it differs from the path, the server renames the stored resource within the same namespace (HTTP 409 if the destination name already exists). +tags: +- name: agents +- name: agent-systems +- name: model-endpoints +- name: tools +- name: secrets + description: GET responses redact `spec.data` entry values as the literal string + `***` and omit `spec.stringData`. PUT merges `***` placeholders for existing keys + (from `spec.data`, and from `spec.stringData` when present) so clients can rename + or edit metadata without re-entering unchanged secrets; new keys still require + real base64 (`spec.data`) or plaintext (`spec.stringData`). PUT may rename the + secret when `metadata.name` differs from the path (HTTP 409 if the target name + exists). +- name: sealed-secrets +- name: memories +- name: context-adapters +- name: eval-datasets +- name: eval-runs +- name: agent-policies +- name: agent-roles +- name: tool-permissions +- name: tool-approvals +- name: task-approvals +- name: tasks +- name: task-schedules +- name: task-webhooks +- name: workers +- name: mcp-servers +- name: a2a + description: Agent-to-Agent (A2A) protocol endpoints. Provides Agent Card discovery, + JSON-RPC task operations, and a registry of local and remote A2A agents. +- name: auth +- name: system +- name: events +paths: + "/v1/agents": + get: + tags: + - agents + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/AgentList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - agents + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/Agent" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/Agent" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agents/watch": + get: + tags: + - agents + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: resourceVersion + in: query + schema: + type: string + - name: name + in: query + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: 'SSE; `event: resource` with WatchResourceEvent JSON in data' + content: + text/event-stream: + schema: + type: string + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agents/{name}/status": + get: + tags: + - agents + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agents + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agents/{name}/logs": + get: + tags: + - agents + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/NamedLogsResponse" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agents/{name}": + get: + tags: + - agents + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/Agent" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agents + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/Agent" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent.yaml#/components/schemas/Agent" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - agents + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-systems": + get: + tags: + - agent-systems + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystemList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - agent-systems + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystem" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystem" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-systems/{name}/status": + get: + tags: + - agent-systems + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agent-systems + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-systems/{name}": + get: + tags: + - agent-systems + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystem" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agent-systems + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystem" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-system.yaml#/components/schemas/AgentSystem" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - agent-systems + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/model-endpoints": + get: + tags: + - model-endpoints + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpointList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - model-endpoints + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/model-endpoints/{name}/status": + get: + tags: + - model-endpoints + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - model-endpoints + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/model-endpoints/{name}": + get: + tags: + - model-endpoints + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - model-endpoints + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/model-endpoint.yaml#/components/schemas/ModelEndpoint" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + summary: Replace a model endpoint (supports rename) + description: |- + Updates the model endpoint identified by the path `name`. If `metadata.name` in the body differs from the path, + the stored resource is renamed to match the body (HTTP 409 if that name already exists in the namespace). + delete: + tags: + - model-endpoints + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tools": + get: + tags: + - tools + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/ToolList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - tools + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/Tool" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/Tool" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tools/{name}/status": + get: + tags: + - tools + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tools + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tools/{name}": + get: + tags: + - tools + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/Tool" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tools + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/Tool" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool.yaml#/components/schemas/Tool" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - tools + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/secrets": + get: + tags: + - secrets + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/SecretList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - secrets + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/Secret" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/Secret" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/secrets/{name}": + get: + tags: + - secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK (sensitive values redacted in `spec.data`; `spec.stringData` + omitted) + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/Secret" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + summary: Get a secret + description: Returns the secret with `spec.data` values redacted as `***`; `spec.stringData` + is omitted (see **secrets** tag). + put: + tags: + - secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/Secret" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/secret.yaml#/components/schemas/Secret" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + summary: Replace a secret (supports rename) + description: See the **secrets** tag for redaction (`***`) and PUT merge behavior; + PUT rename follows the same rules as other namespaced resources. + delete: + tags: + - secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/sealed-secrets": + get: + tags: + - sealed-secrets + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecretList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - sealed-secrets + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/sealed-secrets/{name}": + get: + tags: + - sealed-secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - sealed-secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecret" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - sealed-secrets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/sealing-key/public": + get: + tags: + - sealed-secrets + summary: Get the active sealing public key + description: Returns the active public key used to seal `SealedSecret` manifests. + When no active sealing key is configured, the server returns HTTP 503. + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/sealed-secret.yaml#/components/schemas/SealedSecretPublicKeyResponse" + '503': + description: Sealing key unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/memories": + get: + tags: + - memories + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/MemoryList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - memories + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/Memory" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/Memory" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/memories/{name}/status": + get: + tags: + - memories + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - memories + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/memories/{name}": + get: + tags: + - memories + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/Memory" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - memories + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/Memory" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/memory.yaml#/components/schemas/Memory" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - memories + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/memories/{name}/entries": + get: + tags: + - memories + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: q + in: query + schema: + type: string + - name: prefix + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/MemoryEntriesResponse" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '500': + description: Backend error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/context-adapters": + get: + tags: + - context-adapters + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapterList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - context-adapters + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/context-adapters/{name}": + get: + tags: + - context-adapters + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - context-adapters + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/context-adapter.yaml#/components/schemas/ContextAdapter" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - context-adapters + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-datasets": + get: + tags: + - eval-datasets + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDatasetList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - eval-datasets + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-datasets/{name}/status": + get: + tags: + - eval-datasets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - eval-datasets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-datasets/{name}": + get: + tags: + - eval-datasets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - eval-datasets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-dataset.yaml#/components/schemas/EvalDataset" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - eval-datasets + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs": + get: + tags: + - eval-runs + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRunList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - eval-runs + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/status": + get: + tags: + - eval-runs + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - eval-runs + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}": + get: + tags: + - eval-runs + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - eval-runs + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - eval-runs + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/start": + post: + tags: + - eval-runs + summary: Start an eval run + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/cancel": + post: + tags: + - eval-runs + summary: Cancel an eval run + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/finalize": + post: + tags: + - eval-runs + summary: Finalize an eval run + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/export": + get: + tags: + - eval-runs + summary: Export eval run sample results + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: format + in: query + schema: + type: string + enum: + - json + - csv + default: json + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalSampleResult" + text/csv: + schema: + type: string + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/results": + post: + tags: + - eval-runs + summary: Bulk-import sample annotations + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: object + properties: + sample_name: + type: string + score: + type: + - number + - 'null' + pass: + type: + - boolean + - 'null' + comment: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/{name}/results/{sample_name}": + put: + tags: + - eval-runs + summary: Annotate a single sample result + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: sample_name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + score: + type: + - number + - 'null' + pass: + type: + - boolean + - 'null' + comment: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRun" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/eval-runs/compare": + get: + tags: + - eval-runs + summary: Compare multiple eval runs + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: runs + in: query + required: true + schema: + type: string + description: Comma-separated eval run names (at least two). + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/eval-run.yaml#/components/schemas/EvalRunCompareResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-policies": + get: + tags: + - agent-policies + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicyList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - agent-policies + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-policies/{name}/status": + get: + tags: + - agent-policies + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agent-policies + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-policies/{name}": + get: + tags: + - agent-policies + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agent-policies + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-policy.yaml#/components/schemas/AgentPolicy" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - agent-policies + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-roles": + get: + tags: + - agent-roles + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRoleList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - agent-roles + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRole" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRole" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-roles/{name}": + get: + tags: + - agent-roles + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRole" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - agent-roles + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRole" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/agent-role.yaml#/components/schemas/AgentRole" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - agent-roles + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-permissions": + get: + tags: + - tool-permissions + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermissionList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - tool-permissions + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermission" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermission" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-permissions/{name}": + get: + tags: + - tool-permissions + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermission" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tool-permissions + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermission" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-permission.yaml#/components/schemas/ToolPermission" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - tool-permissions + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-approvals": + get: + tags: + - tool-approvals + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApprovalList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - tool-approvals + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-approvals/{name}": + get: + tags: + - tool-approvals + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - tool-approvals + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tool-approvals + summary: Not supported + parameters: + - name: name + in: path + required: true + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '405': + description: Method not allowed + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-approvals/{name}/approve": + post: + tags: + - tool-approvals + summary: Approve pending tool invocation + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: false + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/ToolApprovalDecisionRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tool-approvals/{name}/deny": + post: + tags: + - tool-approvals + summary: Deny pending tool invocation + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: false + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/ToolApprovalDecisionRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/tool-approval.yaml#/components/schemas/ToolApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-approvals": + get: + tags: + - task-approvals + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApprovalList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - task-approvals + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-approvals/{name}": + get: + tags: + - task-approvals + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - task-approvals + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - task-approvals + summary: Not supported + parameters: + - name: name + in: path + required: true + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '405': + description: Method not allowed + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-approvals/{name}/approve": + post: + tags: + - task-approvals + summary: Approve pending task review + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: false + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TaskApprovalDecisionRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-approvals/{name}/deny": + post: + tags: + - task-approvals + summary: Deny pending task review + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: false + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TaskApprovalDecisionRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-approvals/{name}/request-changes": + post: + tags: + - task-approvals + summary: Request changes on pending task review + description: Reruns the producing agent with reviewer feedback. Requires `comment` + or the legacy `reason` field and returns `409 Conflict` when the checkpoint + disables request_changes or has reached max_review_cycles. + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TaskApprovalRequestChangesRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-approval.yaml#/components/schemas/TaskApproval" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks": + get: + tags: + - tasks + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: offset + in: query + schema: + type: integer + minimum: 0 + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/TaskList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - tasks + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/Task" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/Task" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/watch": + get: + tags: + - tasks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: resourceVersion + in: query + schema: + type: string + - name: name + in: query + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: SSE resource watch for tasks + content: + text/event-stream: + schema: + type: string + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/{name}/status": + get: + tags: + - tasks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tasks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/{name}/logs": + get: + tags: + - tasks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/NamedLogsResponse" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/{name}/messages": + get: + tags: + - tasks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: phase + in: query + schema: + type: string + - name: lifecycle + in: query + schema: + type: string + - name: from_agent + in: query + schema: + type: string + - name: to_agent + in: query + schema: + type: string + - name: branch_id + in: query + schema: + type: string + - name: trace_id + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TaskMessageListResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/{name}/metrics": + get: + tags: + - tasks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: phase + in: query + schema: + type: string + - name: lifecycle + in: query + schema: + type: string + - name: from_agent + in: query + schema: + type: string + - name: to_agent + in: query + schema: + type: string + - name: branch_id + in: query + schema: + type: string + - name: trace_id + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TaskMessageMetricsResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tasks/{name}": + get: + tags: + - tasks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/Task" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - tasks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/Task" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task.yaml#/components/schemas/Task" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - tasks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-schedules": + get: + tags: + - task-schedules + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskScheduleList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - task-schedules + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-schedules/{name}/status": + get: + tags: + - task-schedules + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - task-schedules + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-schedules/{name}": + get: + tags: + - task-schedules + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - task-schedules + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-schedule.yaml#/components/schemas/TaskSchedule" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - task-schedules + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-schedules/watch": + get: + tags: + - task-schedules + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: resourceVersion + in: query + schema: + type: string + - name: name + in: query + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: SSE resource watch for task schedules + content: + text/event-stream: + schema: + type: string + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-webhooks": + get: + tags: + - task-webhooks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: namespace + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhookList" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - task-webhooks + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-webhooks/watch": + get: + tags: + - task-webhooks + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: resourceVersion + in: query + schema: + type: string + - name: name + in: query + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: SSE resource watch for task webhooks + content: + text/event-stream: + schema: + type: string + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-webhooks/{name}/status": + get: + tags: + - task-webhooks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - task-webhooks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/task-webhooks/{name}": + get: + tags: + - task-webhooks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - task-webhooks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/task-webhook.yaml#/components/schemas/TaskWebhook" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - task-webhooks + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/workers": + get: + tags: + - workers + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/WorkerList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - workers + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/Worker" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/Worker" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/workers/{name}/status": + get: + tags: + - workers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - workers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/StatusEnvelope" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/workers/{name}": + get: + tags: + - workers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/Worker" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - workers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/Worker" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/worker.yaml#/components/schemas/Worker" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - workers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '204': + description: Deleted + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/mcp-servers": + get: + tags: + - mcp-servers + parameters: + - name: namespace + in: query + schema: + type: string + - name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + - name: after + in: query + schema: + type: string + - name: labelSelector + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServerList" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - mcp-servers + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServer" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServer" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/mcp-servers/{name}": + get: + tags: + - mcp-servers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServer" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + put: + tags: + - mcp-servers + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + - name: If-Match + in: header + required: false + schema: + type: string + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServer" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/mcp-server.yaml#/components/schemas/McpServer" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + delete: + tags: + - mcp-servers + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: Deleted + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/McpServerDeletedResponse" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/webhook-deliveries/{endpoint_id}": + post: + tags: + - task-webhooks + summary: Inbound webhook (verified per TaskWebhook auth profile generic|github|hmac|shared_token) + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: endpoint_id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + application/octet-stream: + schema: + type: string + format: binary + responses: + '202': + description: Accepted + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/WebhookDeliveryResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Signature verification failed + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Unknown endpoint + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Suspended or conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '500': + description: Server error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/events/watch": + get: + tags: + - events + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: since + in: query + schema: + type: string + - name: source + in: query + schema: + type: string + - name: type + in: query + schema: + type: string + - name: kind + in: query + schema: + type: string + - name: name + in: query + schema: + type: string + - name: namespace + in: query + schema: + type: string + responses: + '200': + description: 'SSE; `event: event` with BusEvent JSON' + content: + text/event-stream: + schema: + type: string + '503': + description: Event bus unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/namespaces": + get: + tags: + - system + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/NamespacesResponse" + '503': + description: Store unavailable + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/capabilities": + get: + tags: + - system + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/CapabilitySnapshot" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/.well-known/agent-card.json": + get: + tags: + - a2a + summary: Get the default Agent Card + description: Returns the A2A Agent Card when exactly one AgentSystem has spec.a2a.enabled=true. + Use the registry or per-system card URLs when multiple systems are exposed. + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/AgentCard" + '404': + description: No single default AgentSystem available + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/.well-known/agent.json": + get: + tags: + - a2a + summary: Get the default Agent Card + description: Returns the A2A Agent Card when exactly one AgentSystem has spec.a2a.enabled=true. + Use the registry or per-system card URLs when multiple systems are exposed. + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/AgentCard" + '404': + description: No single default AgentSystem available + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-systems/{name}/.well-known/agent-card.json": + get: + tags: + - a2a + summary: Get Agent Card for a specific AgentSystem + description: Returns the A2A Agent Card for the named AgentSystem when spec.a2a.enabled=true. + parameters: + - name: name + in: path + required: true + schema: + type: string + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/AgentCard" + '404': + description: AgentSystem not found or not A2A-enabled + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agents/{name}/.well-known/agent-card.json": + get: + tags: + - a2a + summary: Get Agent Card for a specific AgentSystem (legacy path) + description: Legacy alias for /v1/agent-systems/{name}/.well-known/agent-card.json. + parameters: + - name: name + in: path + required: true + schema: + type: string + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/AgentCard" + '404': + description: AgentSystem not found or not A2A-enabled + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/a2a": + post: + tags: + - a2a + summary: A2A JSON-RPC endpoint + description: 'JSON-RPC 2.0 endpoint implementing the A2A protocol. Supports + methods: tasks/send, tasks/get, tasks/cancel, tasks/sendSubscribe.' + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcRequest" + responses: + '200': + description: JSON-RPC response + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/agent-systems/{name}/a2a": + post: + tags: + - a2a + summary: A2A JSON-RPC endpoint for a specific AgentSystem + description: JSON-RPC 2.0 endpoint for the named AgentSystem. The system name + in the path determines routing; no target field is needed in request params. + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcRequest" + responses: + '200': + description: JSON-RPC response + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + parameters: + - name: name + in: path + required: true + schema: + type: string + "/v1/agents/{name}/a2a": + post: + tags: + - a2a + summary: A2A JSON-RPC endpoint for a specific AgentSystem (legacy path) + description: Legacy alias for /v1/agent-systems/{name}/a2a. + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcRequest" + responses: + '200': + description: JSON-RPC response + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2AJsonRpcResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + parameters: + - name: name + in: path + required: true + schema: + type: string + "/v1/a2a/agents": + get: + tags: + - a2a + summary: List A2A agent registry + description: Returns locally exposed A2A-enabled AgentSystem cards visible to + the bearer token plus configured remote agent entries with cache metadata. + security: + - bearerAuth: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/a2a.yaml#/components/schemas/A2ARegistryResponse" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/healthz": + get: + tags: + - system + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/HealthResponse" + "/metrics": + get: + tags: + - system + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: Prometheus text exposition + content: + text/plain: + schema: + type: string + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/config": + get: + tags: + - auth + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthConfigResponse" + '500': + description: Server error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/setup": + post: + tags: + - auth + security: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthCredentialsRequest" + responses: + '201': + description: Created session + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthMeResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Already configured + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '429': + description: Rate limited + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/login": + post: + tags: + - auth + security: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthCredentialsRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthMeResponse" + '401': + description: Invalid credentials + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Setup required + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '429': + description: Rate limited + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/cli-token": + post: + tags: + - auth + summary: Authenticate with username/password and obtain a CLI bearer token + description: Validates credentials against the native user store and mints a + new API bearer token for CLI use. Only available when the server runs in native + auth mode. + security: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthCLITokenRequest" + responses: + '201': + description: Token created + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthCLITokenResponse" + '400': + description: Bad request (non-native mode or missing fields) + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Invalid credentials + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '429': + description: Rate limited + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/logout": + post: + tags: + - auth + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/OkStatusMessage" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/me": + get: + tags: + - auth + security: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthMeResponse" + "/v1/auth/change-password": + post: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthChangePasswordRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + status: + type: string + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '429': + description: Rate limited + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/admin/reset-password": + post: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthResetPasswordRequest" + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + status: + type: string + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '429': + description: Rate limited + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/users": + get: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthUserListResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthUserCreateRequest" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/AuthUserCreateResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/auth/users/{username}": + delete: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/OkStatusMessage" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tokens": + get: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TokenListResponse" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + post: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + requestBody: + required: true + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TokenCreateRequest" + responses: + '201': + description: Created + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/TokenCreateResponse" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '409': + description: Conflict + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + "/v1/tokens/{name}": + delete: + tags: + - auth + security: + - bearerAuth: [] + - sessionCookie: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/OkStatusMessage" + '400': + description: Bad request + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '401': + description: Unauthorized + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '403': + description: Forbidden + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + '404': + description: Not found + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" + default: + description: Error + content: + text/plain: + schema: + "$ref": "./schemas/common.yaml#/components/schemas/PlainTextError" +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + sessionCookie: + type: apiKey + in: cookie + name: orloj_session + description: Session cookie; `__Host-orloj_session` is used for secure sites. diff --git a/openapi/schemas/a2a.yaml b/openapi/schemas/a2a.yaml new file mode 100644 index 0000000..10315fb --- /dev/null +++ b/openapi/schemas/a2a.yaml @@ -0,0 +1,216 @@ +components: + schemas: + AgentCard: + type: object + required: [name, url] + properties: + name: + type: string + description: Human-readable agent name + description: + type: string + description: Agent description + url: + type: string + format: uri + description: A2A JSON-RPC endpoint URL + version: + type: string + protocolVersion: + type: string + description: A2A protocol version + capabilities: + $ref: '#/components/schemas/CardCapabilities' + skills: + type: array + items: + $ref: '#/components/schemas/CardSkill' + authentication: + type: object + properties: + schemes: + type: array + items: + type: string + provider: + type: object + properties: + organization: + type: string + url: + type: string + format: uri + + CardCapabilities: + type: object + properties: + streaming: + type: boolean + pushNotifications: + type: boolean + stateTransitionHistory: + type: boolean + + CardSkill: + type: object + required: [id, name] + properties: + id: + type: string + name: + type: string + description: + type: string + inputSchema: + type: object + additionalProperties: true + tags: + type: array + items: + type: string + + A2AJsonRpcRequest: + type: object + required: [jsonrpc, method] + properties: + jsonrpc: + type: string + enum: ["2.0"] + id: + oneOf: + - type: string + - type: integer + method: + type: string + enum: ["tasks/send", "tasks/get", "tasks/cancel", "tasks/sendSubscribe"] + params: + type: object + + A2AJsonRpcResponse: + type: object + required: [jsonrpc] + properties: + jsonrpc: + type: string + enum: ["2.0"] + id: + oneOf: + - type: string + - type: integer + result: + type: object + error: + $ref: '#/components/schemas/A2AJsonRpcError' + + A2AJsonRpcError: + type: object + required: [code, message] + properties: + code: + type: integer + message: + type: string + data: {} + + A2ATask: + type: object + required: [id, status] + properties: + id: + type: string + status: + $ref: '#/components/schemas/A2AStatus' + artifacts: + type: array + items: + $ref: '#/components/schemas/A2AArtifact' + history: + type: array + items: + $ref: '#/components/schemas/A2AMessage' + metadata: + type: object + additionalProperties: + type: string + + A2AStatus: + type: object + required: [state] + properties: + state: + type: string + enum: [submitted, working, input-required, completed, failed, canceled, rejected] + message: + $ref: '#/components/schemas/A2AMessage' + + A2AArtifact: + type: object + properties: + name: + type: string + description: + type: string + parts: + type: array + items: + $ref: '#/components/schemas/A2APart' + index: + type: integer + + A2AMessage: + type: object + required: [role, parts] + properties: + role: + type: string + parts: + type: array + items: + $ref: '#/components/schemas/A2APart' + + A2APart: + type: object + required: [type] + properties: + type: + type: string + text: + type: string + data: {} + metadata: + type: object + + RemoteAgentEntry: + type: object + required: [name, url] + properties: + name: + type: string + url: + type: string + format: uri + protocolVersion: + type: string + cacheStatus: + type: string + lastRefreshed: + type: string + format: date-time + cacheTTL: + type: string + error: + type: string + card: + $ref: '#/components/schemas/AgentCard' + + A2ARegistryResponse: + type: object + properties: + localAgents: + type: array + items: + $ref: '#/components/schemas/AgentCard' + remoteAgents: + type: array + items: + $ref: '#/components/schemas/RemoteAgentEntry' diff --git a/openapi/schemas/agent-policy.yaml b/openapi/schemas/agent-policy.yaml new file mode 100644 index 0000000..51874c1 --- /dev/null +++ b/openapi/schemas/agent-policy.yaml @@ -0,0 +1,53 @@ +components: + schemas: + AgentPolicySpec: + type: object + properties: + max_tokens_per_run: { type: integer } + allowed_models: + type: array + items: { type: string } + blocked_tools: + type: array + items: { type: string } + apply_mode: { type: string } + target_systems: + type: array + items: { type: string } + target_tasks: + type: array + items: { type: string } + target_agents: + type: array + items: { type: string } + description: Agent names this policy applies to. When set, only listed agents are affected; other agents in the same system/task are unconstrained by this policy. + max_child_depth: + type: integer + description: Maximum nesting depth for tasks created by orloj.task.create. Default 5. + max_child_tasks: + type: integer + description: Maximum number of child tasks a single task execution can create via orloj.task.create. Default 20. + PolicyStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + AgentPolicy: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [AgentPolicy] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/AgentPolicySpec" } + status: { $ref: "#/components/schemas/PolicyStatus" } + AgentPolicyList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/AgentPolicy" } diff --git a/openapi/schemas/agent-role.yaml b/openapi/schemas/agent-role.yaml new file mode 100644 index 0000000..ecccb4f --- /dev/null +++ b/openapi/schemas/agent-role.yaml @@ -0,0 +1,33 @@ +components: + schemas: + AgentRoleSpec: + type: object + properties: + description: { type: string } + permissions: + type: array + items: { type: string } + AgentRoleStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + AgentRole: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [AgentRole] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/AgentRoleSpec" } + status: { $ref: "#/components/schemas/AgentRoleStatus" } + AgentRoleList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/AgentRole" } diff --git a/openapi/schemas/agent-system.yaml b/openapi/schemas/agent-system.yaml new file mode 100644 index 0000000..4a65a2b --- /dev/null +++ b/openapi/schemas/agent-system.yaml @@ -0,0 +1,134 @@ +components: + schemas: + EdgeCondition: + type: object + description: Predicate evaluated against the completing agent's output to determine whether a graph edge should fire. + properties: + output_contains: + type: string + description: Matches if the output contains this string (case-insensitive). + output_not_contains: + type: string + description: Matches if the output does NOT contain this string (case-insensitive). + output_matches: + type: string + description: Matches if the output matches this regex pattern. + default: + type: boolean + description: Marks this edge as the fallback when no conditional edge matches. + output_json_path: + type: string + description: Dot-notation path (e.g. "$.route") to extract a value from JSON output. Requires a comparison operator. + equals: + type: string + description: Matches when the extracted JSON value equals this string. + not_equals: + type: string + description: Matches when the extracted JSON value does NOT equal this string. + contains: + type: string + description: For arrays, matches when any element equals this value. For strings, matches on substring (case-insensitive). + greater_than: + type: string + description: Matches when the extracted numeric JSON value exceeds this threshold. + less_than: + type: string + description: Matches when the extracted numeric JSON value is below this threshold. + GraphRoute: + type: object + properties: + to: { type: string } + labels: { type: object, additionalProperties: { type: string } } + policy: { type: object, additionalProperties: { type: string } } + condition: { $ref: "#/components/schemas/EdgeCondition" } + GraphJoin: + type: object + properties: + mode: { type: string } + quorum_count: { type: integer } + quorum_percent: { type: integer } + on_failure: { type: string } + ReviewCheckpointSpec: + type: object + properties: + checkpoint_id: { type: string } + display_name: { type: string } + reason: { type: string } + ttl: { type: string } + allow_request_changes: { type: boolean, default: true } + max_review_cycles: { type: integer, minimum: 1, default: 3 } + AgentSystemA2ASpec: + type: object + properties: + enabled: + type: boolean + description: Expose this AgentSystem through inbound A2A discovery and JSON-RPC invocation. + auth: + type: string + enum: [public, bearer] + default: bearer + description: >- + Controls whether A2A invoke requires authentication for this system. + "public" allows unauthenticated invoke; "bearer" (default) requires + a valid API token when instance-wide auth is enabled. + GraphEdge: + type: object + properties: + next: { type: string } + edges: + type: array + items: { $ref: "#/components/schemas/GraphRoute" } + join: { $ref: "#/components/schemas/GraphJoin" } + delegates: + type: array + description: Agents dispatched after the node's first execution. Reports flow back and trigger a review re-execution before edges fire. + items: { $ref: "#/components/schemas/GraphRoute" } + delegate_join: + description: Controls how delegate returns are collected before the review phase. + $ref: "#/components/schemas/GraphJoin" + review: + description: Optional human review checkpoint for this node's output before downstream routing continues. + $ref: "#/components/schemas/ReviewCheckpointSpec" + AgentSystemSpec: + type: object + properties: + context_adapter: + type: string + description: > + Optional ContextAdapter resource name. When set, raw task input is passed through the + referenced adapter (Tool-backed sanitization) before the first agent in execution order runs. + agents: + type: array + items: { type: string } + graph: + type: object + additionalProperties: { $ref: "#/components/schemas/GraphEdge" } + completion_review: + description: Optional final human review checkpoint before the task transitions to Succeeded. + $ref: "#/components/schemas/ReviewCheckpointSpec" + a2a: + $ref: "#/components/schemas/AgentSystemA2ASpec" + AgentSystemStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + AgentSystem: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [AgentSystem] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/AgentSystemSpec" } + status: { $ref: "#/components/schemas/AgentSystemStatus" } + AgentSystemList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/AgentSystem" } diff --git a/openapi/schemas/agent.yaml b/openapi/schemas/agent.yaml new file mode 100644 index 0000000..b0712f6 --- /dev/null +++ b/openapi/schemas/agent.yaml @@ -0,0 +1,78 @@ +components: + schemas: + AgentLimits: + type: object + properties: + max_steps: { type: integer } + timeout: { type: string } + MemorySpecInline: + type: object + properties: + ref: { type: string } + type: { type: string } + provider: { type: string } + allow: + type: array + items: { type: string } + AgentExecutionSpec: + type: object + properties: + profile: { type: string } + tool_sequence: + type: array + items: { type: string } + required_output_markers: + type: array + items: { type: string } + duplicate_tool_call_policy: { type: string } + on_contract_violation: { type: string } + tool_use_behavior: { type: string } + output_schema: + type: object + description: JSON Schema constraining the agent's output. Passed to model providers for structured output enforcement. + additionalProperties: true + AgentSpec: + type: object + properties: + model_ref: { type: string } + fallback_model_refs: + type: array + items: { type: string } + description: Ordered list of fallback ModelEndpoint refs tried when the primary model_ref fails with a retryable error. + prompt: { type: string } + tools: + type: array + items: { type: string } + allowed_tools: + type: array + items: { type: string } + roles: + type: array + items: { type: string } + memory: { $ref: "#/components/schemas/MemorySpecInline" } + execution: { $ref: "#/components/schemas/AgentExecutionSpec" } + limits: { $ref: "#/components/schemas/AgentLimits" } + AgentStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + Agent: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Agent] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/AgentSpec" } + status: { $ref: "#/components/schemas/AgentStatus" } + AgentList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Agent" } diff --git a/openapi/schemas/common.yaml b/openapi/schemas/common.yaml new file mode 100644 index 0000000..d7242d0 --- /dev/null +++ b/openapi/schemas/common.yaml @@ -0,0 +1,313 @@ +# Shared schemas (JSON names match Go json tags). +components: + schemas: + ObjectMeta: + type: object + required: [name] + properties: + name: { type: string } + namespace: { type: string, default: default } + labels: { type: object, additionalProperties: { type: string } } + annotations: { type: object, additionalProperties: { type: string } } + resourceVersion: { type: string } + generation: { type: integer, format: int64 } + createdAt: { type: string } + ListMeta: + type: object + properties: + continue: + type: string + description: Cursor for next page as scoped namespace/name of the last scanned item; bare names from older clients are still accepted as ?after= values. + PlainTextError: + type: string + description: Error body when Content-Type is text/plain. + HealthResponse: + type: object + required: [status] + properties: + status: { type: string, enum: [ok] } + NamespacesResponse: + type: object + required: [namespaces] + properties: + namespaces: { type: array, items: { type: string } } + Capability: + type: object + required: [id, enabled] + properties: + id: { type: string } + enabled: { type: boolean } + description: { type: string } + source: { type: string } + CapabilitySnapshot: + type: object + required: [generated_at, capabilities] + properties: + generated_at: { type: string } + capabilities: + type: array + items: { $ref: "#/components/schemas/Capability" } + AuthConfigResponse: + type: object + properties: + mode: { type: string } + setup_required: { type: boolean } + setup_token_required: + type: boolean + description: When true, native initial setup requires `setup_token` in `POST /v1/auth/setup` (matches `ORLOJ_SETUP_TOKEN` on the server). + login_methods: { type: array, items: { type: string } } + AuthMeResponse: + type: object + properties: + authenticated: { type: boolean } + username: { type: string } + name: { type: string } + role: { type: string } + method: { type: string } + AuthCredentialsRequest: + type: object + properties: + username: { type: string } + password: { type: string } + setup_token: { type: string } + AuthCLITokenRequest: + type: object + required: [username, password] + properties: + username: { type: string } + password: { type: string } + AuthCLITokenResponse: + type: object + properties: + name: { type: string, description: "Generated token name" } + role: { type: string, description: "Role inherited from the user account" } + token: { type: string, description: "Bearer token value (shown only once)" } + username: { type: string, description: "Authenticated username" } + created_at: { type: string, format: date-time } + AuthChangePasswordRequest: + type: object + properties: + current_password: { type: string } + new_password: { type: string } + AuthResetPasswordRequest: + type: object + required: [username, new_password] + properties: + username: { type: string } + new_password: { type: string } + TokenCreateRequest: + type: object + required: [name, role] + properties: + name: { type: string } + role: + type: string + enum: [admin, writer, reader, controller, a2a] + a2a_agent_systems: + type: array + description: Required when role is `a2a`; AgentSystem refs this token may invoke. Bare names resolve in the default namespace. + items: { type: string } + TokenMetadata: + type: object + properties: + name: { type: string } + role: + type: string + enum: [admin, writer, reader, controller, a2a] + created_at: { type: string } + a2a_agent_systems: + type: array + items: { type: string } + TokenCreateResponse: + type: object + properties: + name: { type: string } + role: + type: string + enum: [admin, writer, reader, controller, a2a] + created_at: { type: string } + token: { type: string } + a2a_agent_systems: + type: array + items: { type: string } + TokenListResponse: + type: object + properties: + items: + type: array + items: { $ref: "#/components/schemas/TokenMetadata" } + AuthUserCreateRequest: + type: object + required: [username] + properties: + username: { type: string } + role: { type: string } + AuthUserResponse: + type: object + properties: + username: { type: string } + role: { type: string } + created_at: { type: string } + updated_at: { type: string } + AuthUserCreateResponse: + type: object + properties: + username: { type: string } + role: { type: string } + created_at: { type: string } + updated_at: { type: string } + password: { type: string } + AuthUserListResponse: + type: object + properties: + items: + type: array + items: { $ref: "#/components/schemas/AuthUserResponse" } + OkStatusMessage: + type: object + properties: + status: { type: string } + WebhookDeliveryResponse: + type: object + properties: + accepted: { type: boolean } + duplicate: { type: boolean } + event_id: { type: string } + task: { type: string } + message: { type: string } + WatchResourceEvent: + type: object + properties: + type: { type: string } + resource: { type: object, additionalProperties: true } + BusEvent: + type: object + properties: + id: { type: integer, format: int64 } + timestamp: { type: string } + source: { type: string } + type: { type: string } + kind: { type: string } + name: { type: string } + namespace: { type: string } + action: { type: string } + message: { type: string } + data: { nullable: true } + ToolApprovalDecisionRequest: + type: object + properties: + decided_by: { type: string } + comment: { type: string } + reason: { type: string } + TaskApprovalDecisionRequest: + type: object + properties: + decided_by: { type: string } + comment: { type: string } + reason: { type: string } + TaskApprovalRequestChangesRequest: + type: object + properties: + decided_by: { type: string } + comment: { type: string } + reason: { type: string } + anyOf: + - required: [comment] + - required: [reason] + MemoryEntriesResponse: + type: object + properties: + entries: + type: array + items: { type: object, additionalProperties: true } + count: { type: integer } + NamedLogsResponse: + type: object + properties: + name: { type: string } + logs: + type: array + items: { type: string } + TaskMessageListResponse: + type: object + properties: + name: { type: string } + namespace: { type: string } + total: { type: integer } + filtered_from: { type: integer } + lifecycle_counts: { type: object, additionalProperties: { type: integer } } + messages: + type: array + items: { $ref: "task.yaml#/components/schemas/TaskMessage" } + TaskMessageTotals: + type: object + properties: + messages: { type: integer } + queued: { type: integer } + running: { type: integer } + retrypending: { type: integer } + succeeded: { type: integer } + deadletter: { type: integer } + in_flight: { type: integer } + retry_count: { type: integer } + deadletters: { type: integer } + latency_ms_avg: { type: integer, format: int64 } + latency_ms_p95: { type: integer, format: int64 } + latency_sample_size: { type: integer } + TaskMessageAgentMetric: + type: object + properties: + agent: { type: string } + inbound: { type: integer } + outbound: { type: integer } + queued: { type: integer } + running: { type: integer } + retrypending: { type: integer } + succeeded: { type: integer } + deadletter: { type: integer } + in_flight: { type: integer } + retry_count: { type: integer } + deadletters: { type: integer } + latency_ms_avg: { type: integer, format: int64 } + latency_ms_p95: { type: integer, format: int64 } + latency_sample_size: { type: integer } + TaskMessageEdgeMetric: + type: object + properties: + from_agent: { type: string } + to_agent: { type: string } + messages: { type: integer } + queued: { type: integer } + running: { type: integer } + retrypending: { type: integer } + succeeded: { type: integer } + deadletter: { type: integer } + in_flight: { type: integer } + retry_count: { type: integer } + deadletters: { type: integer } + latency_ms_avg: { type: integer, format: int64 } + latency_ms_p95: { type: integer, format: int64 } + latency_sample_size: { type: integer } + TaskMessageMetricsResponse: + type: object + properties: + name: { type: string } + namespace: { type: string } + generated_at: { type: string } + totals: { $ref: "#/components/schemas/TaskMessageTotals" } + per_agent: + type: array + items: { $ref: "#/components/schemas/TaskMessageAgentMetric" } + per_edge: + type: array + items: { $ref: "#/components/schemas/TaskMessageEdgeMetric" } + filters: { type: object, additionalProperties: true } + McpServerDeletedResponse: + type: object + properties: + deleted: { type: string } + StatusEnvelope: + type: object + properties: + metadata: { $ref: "#/components/schemas/ObjectMeta" } + status: { type: object, additionalProperties: true } diff --git a/openapi/schemas/context-adapter.yaml b/openapi/schemas/context-adapter.yaml new file mode 100644 index 0000000..22fc293 --- /dev/null +++ b/openapi/schemas/context-adapter.yaml @@ -0,0 +1,33 @@ +components: + schemas: + ContextAdapterSpec: + type: object + required: [tool_ref] + properties: + tool_ref: { type: string } + on_error: + type: string + enum: [reject, passthrough] + ContextAdapterStatus: + type: object + properties: + phase: { type: string } + message: { type: string } + ContextAdapter: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [ContextAdapter] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/ContextAdapterSpec" } + status: { $ref: "#/components/schemas/ContextAdapterStatus" } + ContextAdapterList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/ContextAdapter" } diff --git a/openapi/schemas/eval-dataset.yaml b/openapi/schemas/eval-dataset.yaml new file mode 100644 index 0000000..b82e874 --- /dev/null +++ b/openapi/schemas/eval-dataset.yaml @@ -0,0 +1,63 @@ +components: + schemas: + EvalScoringConfig: + type: object + properties: + strategy: + type: string + enum: [exact_match, llm_judge, manual, custom] + model_ref: { type: string } + rubric: { type: string } + tool_ref: { type: string } + EvalExpected: + type: object + properties: + output_contains: { type: string } + output_not_contains: { type: string } + output_matches: { type: string } + output_json_path: { type: string } + equals: { type: string } + not_equals: { type: string } + contains: { type: string } + greater_than: { type: string } + less_than: { type: string } + EvalSample: + type: object + required: [name, input] + properties: + name: { type: string } + input: + type: object + additionalProperties: { type: string } + expected: { $ref: "#/components/schemas/EvalExpected" } + scoring: { $ref: "#/components/schemas/EvalScoringConfig" } + EvalDatasetSpec: + type: object + required: [samples] + properties: + description: { type: string } + samples: + type: array + items: { $ref: "#/components/schemas/EvalSample" } + EvalDatasetStatus: + type: object + properties: + phase: { type: string } + EvalDataset: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [EvalDataset] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/EvalDatasetSpec" } + status: { $ref: "#/components/schemas/EvalDatasetStatus" } + EvalDatasetList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/EvalDataset" } diff --git a/openapi/schemas/eval-run.yaml b/openapi/schemas/eval-run.yaml new file mode 100644 index 0000000..edd0fb0 --- /dev/null +++ b/openapi/schemas/eval-run.yaml @@ -0,0 +1,111 @@ +components: + schemas: + AgentOverride: + type: object + properties: + prompt: { type: string } + model_ref: { type: string } + EvalSampleResult: + type: object + required: [sample_name] + properties: + sample_name: { type: string } + task_name: { type: string } + score: { type: [number, "null"] } + pass: { type: [boolean, "null"] } + error: { type: string } + latency: { type: string } + tokens: { type: integer } + output: { type: string } + reasoning: { type: string } + comment: { type: string } + EvalSummary: + type: object + properties: + pass_rate: { type: number } + mean_score: { type: number } + p50_latency: { type: string } + p95_latency: { type: string } + total_tokens: { type: integer } + total_cost: { type: number } + EvalRunSpec: + type: object + required: [dataset_ref, system] + properties: + dataset_ref: { type: string } + system: { type: string } + model_endpoint_override: { type: string } + agent_overrides: + type: object + additionalProperties: { $ref: "#/components/schemas/AgentOverride" } + scoring: { $ref: "eval-dataset.yaml#/components/schemas/EvalScoringConfig" } + concurrency: { type: integer, default: 5 } + timeout: { type: string } + labels: + type: object + additionalProperties: { type: string } + suspended: { type: boolean, default: false } + EvalRunStatus: + type: object + properties: + phase: + type: string + enum: [Pending, Running, Scoring, PendingReview, Succeeded, Failed, Cancelled] + totalSamples: { type: integer } + completedSamples: { type: integer } + passedSamples: { type: integer } + failedSamples: { type: integer } + erroredSamples: { type: integer } + results: + type: array + items: { $ref: "#/components/schemas/EvalSampleResult" } + summary: { $ref: "#/components/schemas/EvalSummary" } + datasetGeneration: { type: integer } + startedAt: { type: string, format: date-time } + completedAt: { type: string, format: date-time } + cancelledAt: { type: string, format: date-time } + EvalRun: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [EvalRun] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/EvalRunSpec" } + status: { $ref: "#/components/schemas/EvalRunStatus" } + EvalRunList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/EvalRun" } + EvalRunCompareResponse: + type: object + properties: + runs: + type: array + items: { type: string } + summary: + type: object + additionalProperties: + type: object + properties: + pass_rate: { type: number } + mean_score: { type: number } + total_tokens: { type: integer } + samples: + type: array + items: + type: object + properties: + name: { type: string } + results: + type: object + additionalProperties: + type: object + properties: + score: { type: [number, "null"] } + pass: { type: [boolean, "null"] } diff --git a/openapi/schemas/mcp-server.yaml b/openapi/schemas/mcp-server.yaml new file mode 100644 index 0000000..b7356ac --- /dev/null +++ b/openapi/schemas/mcp-server.yaml @@ -0,0 +1,81 @@ +components: + schemas: + McpServerEnvVar: + type: object + required: [name] + properties: + name: { type: string } + value: { type: string } + secretRef: { type: string } + mountPath: { type: string, description: "Absolute path inside the container where the resolved value is written as a file. Only valid when spec.image is set." } + McpToolFilter: + type: object + properties: + include: + type: array + items: { type: string } + McpReconnectPolicy: + type: object + properties: + max_attempts: { type: integer } + backoff: { type: string } + McpServerSpec: + type: object + required: [transport] + properties: + transport: { type: string, enum: [stdio, http] } + command: { type: string } + args: + type: array + items: { type: string } + env: + type: array + items: { $ref: "#/components/schemas/McpServerEnvVar" } + endpoint: { type: string } + allowPrivate: + type: boolean + description: | + Permit this MCP server's HTTP transport to connect to RFC 1918 / + ULA / carrier-grade NAT addresses, such as trusted in-cluster + services. Loopback, link-local, cloud metadata, and unspecified + addresses remain blocked regardless. Defaults to false and has no + effect on stdio transport. + image: { type: string, description: "Container image for stdio transport. When set, the MCP server runs inside a Docker container." } + image_pull_secret: { type: string, description: "Name of a Secret containing registry credentials for pulling spec.image." } + idle_timeout: { type: string, description: "Duration after which an idle session is reaped (e.g. 5m). 0 means never evict.", default: "0" } + auth: { $ref: "tool.yaml#/components/schemas/ToolAuth" } + tool_filter: { $ref: "#/components/schemas/McpToolFilter" } + reconnect: { $ref: "#/components/schemas/McpReconnectPolicy" } + resources: { $ref: "tool.yaml#/components/schemas/ContainerResources" } + default_tool_runtime: { $ref: "tool.yaml#/components/schemas/ToolRuntimePolicy" } + McpServerStatus: + type: object + properties: + phase: { type: string } + discoveredTools: + type: array + items: { type: string } + generatedTools: + type: array + items: { type: string } + lastSyncedAt: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + McpServer: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [McpServer] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/McpServerSpec" } + status: { $ref: "#/components/schemas/McpServerStatus" } + McpServerList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/McpServer" } diff --git a/openapi/schemas/memory.yaml b/openapi/schemas/memory.yaml new file mode 100644 index 0000000..2614c0f --- /dev/null +++ b/openapi/schemas/memory.yaml @@ -0,0 +1,39 @@ +components: + schemas: + MemoryAuthConfig: + type: object + properties: + secretRef: { type: string } + MemoryConfig: + type: object + properties: + type: { type: string } + provider: { type: string } + embedding_model: { type: string } + endpoint: { type: string } + endpoint_secret_ref: { type: string } + auth: { $ref: "#/components/schemas/MemoryAuthConfig" } + MemoryStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + Memory: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Memory] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/MemoryConfig" } + status: { $ref: "#/components/schemas/MemoryStatus" } + MemoryList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Memory" } diff --git a/openapi/schemas/model-endpoint.yaml b/openapi/schemas/model-endpoint.yaml new file mode 100644 index 0000000..748b28a --- /dev/null +++ b/openapi/schemas/model-endpoint.yaml @@ -0,0 +1,47 @@ +components: + schemas: + ModelEndpointAuth: + type: object + properties: + secretRef: { type: string } + ModelEndpointSpec: + type: object + properties: + provider: { type: string } + base_url: { type: string } + default_model: { type: string } + options: { type: object, additionalProperties: { type: string } } + auth: { $ref: "#/components/schemas/ModelEndpointAuth" } + allowPrivate: + type: boolean + description: | + Permit outbound connections from this endpoint's gateway to + RFC 1918 / ULA / carrier-grade NAT addresses (e.g. a + self-hosted Ollama, vLLM, LM Studio, or LiteLLM proxy on a + private network). Loopback, link-local, cloud metadata, and + unspecified addresses remain blocked regardless. Defaults: + ollama -> true, all other providers -> false. + ModelEndpointStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + ModelEndpoint: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [ModelEndpoint] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/ModelEndpointSpec" } + status: { $ref: "#/components/schemas/ModelEndpointStatus" } + ModelEndpointList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/ModelEndpoint" } diff --git a/openapi/schemas/sealed-secret.yaml b/openapi/schemas/sealed-secret.yaml new file mode 100644 index 0000000..7c64150 --- /dev/null +++ b/openapi/schemas/sealed-secret.yaml @@ -0,0 +1,66 @@ +components: + schemas: + SealedValue: + type: object + properties: + keyId: { type: string } + wrappedKey: + type: string + description: Base64-encoded RSA-OAEP wrapped AES data key. + ciphertext: + type: string + description: Base64-encoded `nonce || aes_gcm_ciphertext`. + SealedSecretTemplateSecret: + type: object + properties: + labels: + type: object + additionalProperties: { type: string } + annotations: + type: object + additionalProperties: { type: string } + SealedSecretSpec: + type: object + properties: + encryptedData: + type: object + additionalProperties: + $ref: "#/components/schemas/SealedValue" + template: + $ref: "#/components/schemas/SealedSecretTemplateSecret" + SealedSecretStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + SealedSecret: + type: object + description: |- + Git-safe encrypted secret manifest. `orlojd` decrypts `spec.encryptedData` + and reconciles it into a normal `Secret` with the same name and namespace. + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [SealedSecret] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/SealedSecretSpec" } + status: { $ref: "#/components/schemas/SealedSecretStatus" } + SealedSecretList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/SealedSecret" } + SealedSecretPublicKeyResponse: + type: object + required: [keyId, algorithm, publicKeyPEM] + properties: + keyId: { type: string } + algorithm: { type: string } + publicKeyPEM: + type: string + description: PEM-encoded RSA public key for sealing secrets client-side. diff --git a/openapi/schemas/secret.yaml b/openapi/schemas/secret.yaml new file mode 100644 index 0000000..86abef8 --- /dev/null +++ b/openapi/schemas/secret.yaml @@ -0,0 +1,41 @@ +components: + schemas: + SecretSpec: + type: object + properties: + data: + type: object + additionalProperties: { type: string } + description: Base64-encoded secret entries. GET responses replace values with `***`; PUT merges `***` for keys that already exist on the server. + stringData: + type: object + additionalProperties: { type: string } + description: Write-only plaintext entries (normalized into `data` as base64). Omitted on GET; on PUT, if present with `***`, values are merged from stored keys like `data`. + SecretStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + Secret: + type: object + description: |- + Credential resource. GET returns `spec.data` values redacted as `***` and omits `spec.stringData`. + PUT may send those placeholders for existing keys; the server merges stored values before validation (for `spec.data`, and for `spec.stringData` when present). + PUT rename when `metadata.name` differs from the URL follows the same namespaced PUT rules as other resources (see root `info` and the **secrets** tag in the OpenAPI document). + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Secret] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/SecretSpec" } + status: { $ref: "#/components/schemas/SecretStatus" } + SecretList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Secret" } diff --git a/openapi/schemas/task-approval.yaml b/openapi/schemas/task-approval.yaml new file mode 100644 index 0000000..31f5319 --- /dev/null +++ b/openapi/schemas/task-approval.yaml @@ -0,0 +1,49 @@ +components: + schemas: + TaskApprovalSpec: + type: object + required: [task_ref, checkpoint_id] + properties: + task_ref: { type: string } + checkpoint_id: { type: string } + checkpoint_type: { type: string, enum: [agent_output, task_output] } + agent: { type: string } + reason: { type: string } + ttl: { type: string } + allow_request_changes: { type: boolean, default: true } + max_review_cycles: { type: integer, minimum: 1, default: 3 } + review_cycle: { type: integer, minimum: 1, default: 1 } + supersedes: { type: string } + output: + type: ["string", "null"] + output_format: { type: string, enum: [text, json] } + resume_context: + type: object + additionalProperties: true + TaskApprovalStatus: + type: object + properties: + phase: { type: string, enum: [Pending, Approved, Denied, ChangesRequested, Expired] } + decision: { type: string, enum: [approved, denied, request_changes] } + decided_by: { type: string } + decided_at: { type: string, format: date-time } + comment: { type: string } + expires_at: { type: string, format: date-time } + TaskApproval: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [TaskApproval] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/TaskApprovalSpec" } + status: { $ref: "#/components/schemas/TaskApprovalStatus" } + TaskApprovalList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/TaskApproval" } diff --git a/openapi/schemas/task-schedule.yaml b/openapi/schemas/task-schedule.yaml new file mode 100644 index 0000000..951e31f --- /dev/null +++ b/openapi/schemas/task-schedule.yaml @@ -0,0 +1,45 @@ +components: + schemas: + TaskScheduleSpec: + type: object + properties: + task_ref: { type: string, description: "Reference to a template Task (name or namespace/name). Mutually exclusive with task_template." } + task_template: { $ref: "task.yaml#/components/schemas/TaskSpec", description: "Inline task spec used instead of a separate template Task. Mutually exclusive with task_ref." } + schedule: { type: string } + time_zone: { type: string } + suspend: { type: boolean } + starting_deadline_seconds: { type: integer } + concurrency_policy: { type: string } + successful_history_limit: { type: integer } + failed_history_limit: { type: integer } + TaskScheduleStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + lastScheduleTime: { type: string } + lastSuccessfulTime: { type: string } + nextScheduleTime: { type: string } + lastTriggeredTask: { type: string } + activeRuns: + type: array + items: { type: string } + observedGeneration: { type: integer, format: int64 } + TaskSchedule: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [TaskSchedule] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/TaskScheduleSpec" } + status: { $ref: "#/components/schemas/TaskScheduleStatus" } + TaskScheduleList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/TaskSchedule" } diff --git a/openapi/schemas/task-webhook.yaml b/openapi/schemas/task-webhook.yaml new file mode 100644 index 0000000..bfae109 --- /dev/null +++ b/openapi/schemas/task-webhook.yaml @@ -0,0 +1,68 @@ +components: + schemas: + TaskWebhookAuthSpec: + type: object + properties: + profile: { type: string, description: "Auth profile: generic, github, hmac, or shared_token" } + secret_ref: { type: string } + signature_header: { type: string } + signature_prefix: { type: string } + timestamp_header: { type: string } + max_skew_seconds: { type: integer } + algorithm: { type: string, description: "HMAC algorithm: sha256 (default), sha1, or sha512. Used with hmac profile." } + payload_format: { type: string, description: "HMAC payload construction: body, timestamp_dot_body, or prefix_timestamp_body. Used with hmac profile." } + payload_prefix: { type: string, description: "Literal prefix for prefix_timestamp_body format (e.g. v0 for Slack)." } + payload_separator: { type: string, description: "Separator between payload parts (default dot). Used with prefix_timestamp_body." } + signature_encoding: { type: string, description: "Signature encoding: hex (default) or base64. Used with hmac profile." } + header_format: { type: string, description: "How to parse the signature header: plain (default) or kv_pairs (comma-separated key=value)." } + signature_key: { type: string, description: "Key holding the signature in a kv_pairs header (e.g. v1 for Stripe)." } + timestamp_key: { type: string, description: "Key holding the timestamp in a kv_pairs header (e.g. t for Stripe)." } + TaskWebhookIdempotency: + type: object + properties: + event_id_header: { type: string, description: "HTTP header name containing the event ID for deduplication (e.g. X-Event-Id, X-GitHub-Delivery)." } + event_id_from_body: { type: string, description: "Dot-separated field path to extract the event ID from the JSON request body (e.g. update_id, data.id). Used when the sender puts the dedup key in the body instead of a header." } + dedupe_window_seconds: { type: integer } + TaskWebhookPayloadSpec: + type: object + properties: + mode: { type: string } + input_key: { type: string } + TaskWebhookSpec: + type: object + properties: + task_ref: { type: string, description: "Reference to a template Task (name or namespace/name). Mutually exclusive with task_template." } + task_template: { $ref: "task.yaml#/components/schemas/TaskSpec", description: "Inline task spec used instead of a separate template Task. Mutually exclusive with task_ref." } + suspend: { type: boolean } + auth: { $ref: "#/components/schemas/TaskWebhookAuthSpec" } + idempotency: { $ref: "#/components/schemas/TaskWebhookIdempotency" } + payload: { $ref: "#/components/schemas/TaskWebhookPayloadSpec" } + TaskWebhookStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + endpointID: { type: string } + endpointPath: { type: string } + lastDeliveryTime: { type: string } + lastEventID: { type: string } + lastTriggeredTask: { type: string } + acceptedCount: { type: integer, format: int64 } + duplicateCount: { type: integer, format: int64 } + rejectedCount: { type: integer, format: int64 } + TaskWebhook: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [TaskWebhook] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/TaskWebhookSpec" } + status: { $ref: "#/components/schemas/TaskWebhookStatus" } + TaskWebhookList: + type: object + properties: + items: + type: array + items: { $ref: "#/components/schemas/TaskWebhook" } diff --git a/openapi/schemas/task.yaml b/openapi/schemas/task.yaml new file mode 100644 index 0000000..a975b2a --- /dev/null +++ b/openapi/schemas/task.yaml @@ -0,0 +1,196 @@ +components: + schemas: + TaskRetryPolicy: + type: object + properties: + max_attempts: { type: integer } + backoff: { type: string } + TaskMessageRetryPolicy: + type: object + properties: + max_attempts: { type: integer } + backoff: { type: string } + max_backoff: { type: string } + jitter: { type: string } + non_retryable: + type: array + items: { type: string } + TaskRequirements: + type: object + properties: + region: { type: string } + gpu: { type: boolean } + model: { type: string } + TaskSpec: + type: object + properties: + system: { type: string } + mode: { type: string } + input: { type: object, additionalProperties: { type: string } } + priority: { type: string } + max_turns: { type: integer } + retry: { $ref: "#/components/schemas/TaskRetryPolicy" } + message_retry: { $ref: "#/components/schemas/TaskMessageRetryPolicy" } + requirements: { $ref: "#/components/schemas/TaskRequirements" } + TaskTraceEvent: + type: object + properties: + timestamp: { type: string } + step_id: { type: string } + attempt: { type: integer } + step: { type: integer } + branch_id: { type: string } + type: { type: string } + agent: { type: string } + tool: { type: string } + tool_contract_version: { type: string } + tool_request_id: { type: string } + tool_attempt: { type: integer } + error_code: { type: string } + error_reason: { type: string } + retryable: { type: boolean } + message: { type: string } + latency_ms: { type: integer, format: int64 } + tokens: { type: integer } + input_tokens: { type: integer } + output_tokens: { type: integer } + token_usage_source: { type: string } + tool_calls: { type: integer } + memory_writes: { type: integer } + tool_auth_profile: { type: string } + tool_auth_secret_ref: { type: string } + TaskHistoryEvent: + type: object + properties: + timestamp: { type: string } + type: { type: string } + worker: { type: string } + message: { type: string } + TaskMessage: + type: object + properties: + timestamp: { type: string } + message_id: { type: string } + idempotency_key: { type: string } + task_id: { type: string } + attempt: { type: integer } + system: { type: string } + from_agent: { type: string } + to_agent: { type: string } + branch_id: { type: string } + parent_branch_id: { type: string } + type: { type: string } + content: { type: string } + trace_id: { type: string } + parent_id: { type: string } + phase: { type: string } + attempts: { type: integer } + max_attempts: { type: integer } + last_error: { type: string } + worker: { type: string } + processed_at: { type: string } + next_attempt_at: { type: string } + TaskMessageIdempotency: + type: object + properties: + key: { type: string } + message_id: { type: string } + state: { type: string } + updated_at: { type: string } + expires_at: { type: string } + worker: { type: string } + TaskJoinSource: + type: object + properties: + message_id: { type: string } + from_agent: { type: string } + branch_id: { type: string } + timestamp: { type: string } + payload: { type: string } + TaskJoinState: + type: object + properties: + attempt: { type: integer } + node: { type: string } + mode: { type: string } + expected: { type: integer } + quorum_required: { type: integer } + activated: { type: boolean } + activated_at: { type: string } + activated_by: { type: string } + sources: + type: array + items: { $ref: "#/components/schemas/TaskJoinSource" } + TaskDelegationState: + type: object + properties: + attempt: { type: integer } + node: { type: string } + mode: { type: string } + expected: { type: integer } + quorum_required: { type: integer } + activated: { type: boolean } + activated_at: { type: string } + activated_by: { type: string } + sources: + type: array + items: { $ref: "#/components/schemas/TaskJoinSource" } + TaskBlockedOn: + type: object + properties: + kind: { type: string } + name: { type: string } + reason: { type: string } + TaskStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + startedAt: { type: string } + completedAt: { type: string } + nextAttemptAt: { type: string } + attempts: { type: integer } + output: { type: object, additionalProperties: { type: string } } + assignedWorker: { type: string } + claimedBy: { type: string } + leaseUntil: { type: string } + lastHeartbeat: { type: string } + trace: + type: array + items: { $ref: "#/components/schemas/TaskTraceEvent" } + history: + type: array + items: { $ref: "#/components/schemas/TaskHistoryEvent" } + messages: + type: array + items: { $ref: "#/components/schemas/TaskMessage" } + message_idempotency: + type: array + items: { $ref: "#/components/schemas/TaskMessageIdempotency" } + join_states: + type: array + items: { $ref: "#/components/schemas/TaskJoinState" } + delegation_states: + type: array + items: { $ref: "#/components/schemas/TaskDelegationState" } + blocked_on: + $ref: "#/components/schemas/TaskBlockedOn" + observedGeneration: { type: integer, format: int64 } + Task: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Task] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/TaskSpec" } + status: { $ref: "#/components/schemas/TaskStatus" } + TaskList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Task" } diff --git a/openapi/schemas/tool-approval.yaml b/openapi/schemas/tool-approval.yaml new file mode 100644 index 0000000..b3c2bcb --- /dev/null +++ b/openapi/schemas/tool-approval.yaml @@ -0,0 +1,40 @@ +components: + schemas: + ToolApprovalSpec: + type: object + required: [task_ref, tool] + properties: + task_ref: { type: string } + tool: { type: string } + operation_class: { type: string } + agent: { type: string } + input: { type: string } + reason: { type: string } + ttl: { type: string } + ToolApprovalStatus: + type: object + properties: + phase: { type: string } + decision: { type: string } + decided_by: { type: string } + decided_at: { type: string } + comment: { type: string } + expires_at: { type: string } + ToolApproval: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [ToolApproval] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/ToolApprovalSpec" } + status: { $ref: "#/components/schemas/ToolApprovalStatus" } + ToolApprovalList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/ToolApproval" } diff --git a/openapi/schemas/tool-permission.yaml b/openapi/schemas/tool-permission.yaml new file mode 100644 index 0000000..83b5b51 --- /dev/null +++ b/openapi/schemas/tool-permission.yaml @@ -0,0 +1,47 @@ +components: + schemas: + OperationRule: + type: object + properties: + operation_class: { type: string } + verdict: { type: string } + ToolPermissionSpec: + type: object + properties: + tool_ref: { type: string } + action: { type: string } + required_permissions: + type: array + items: { type: string } + match_mode: { type: string } + apply_mode: { type: string } + target_agents: + type: array + items: { type: string } + operation_rules: + type: array + items: { $ref: "#/components/schemas/OperationRule" } + ToolPermissionStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + ToolPermission: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [ToolPermission] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/ToolPermissionSpec" } + status: { $ref: "#/components/schemas/ToolPermissionStatus" } + ToolPermissionList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/ToolPermission" } diff --git a/openapi/schemas/tool.yaml b/openapi/schemas/tool.yaml new file mode 100644 index 0000000..699d385 --- /dev/null +++ b/openapi/schemas/tool.yaml @@ -0,0 +1,120 @@ +components: + schemas: + ToolRetryPolicy: + type: object + properties: + max_attempts: { type: integer } + backoff: { type: string } + max_backoff: { type: string } + jitter: { type: string } + ToolRuntimePolicy: + type: object + properties: + timeout: { type: string } + isolation_mode: { type: string } + retry: { $ref: "#/components/schemas/ToolRetryPolicy" } + ToolAuth: + type: object + properties: + profile: { type: string } + secretRef: { type: string } + headerName: { type: string } + tokenURL: { type: string } + scopes: + type: array + items: { type: string } + ToolCliEnvRef: + type: object + required: [name, secretRef] + properties: + name: { type: string } + secretRef: { type: string } + key: { type: string } + ContainerResources: + type: object + description: "Per-tool or per-McpServer container resource overrides. When set, these take precedence over global --tool-container-{memory,cpus,pids-limit} flags." + properties: + memory: { type: string, description: "Container memory limit (e.g. 128m, 1g). Overrides --tool-container-memory." } + cpus: { type: string, description: "Container CPU limit (e.g. 0.50, 1.0). Overrides --tool-container-cpus." } + pids_limit: { type: integer, description: "Container PIDs limit. Overrides --tool-container-pids-limit." } + ToolCliSpec: + type: object + properties: + command: { type: string } + args: + type: array + items: { type: string } + image: { type: string } + image_pull_secret: { type: string, description: "Name of a Secret containing registry credentials for pulling cli.image." } + network: { type: string } + stdin_from_input: { type: boolean } + output: { type: string } + working_dir: { type: string } + env: + type: object + additionalProperties: { type: string } + env_from: + type: array + items: { $ref: "#/components/schemas/ToolCliEnvRef" } + resources: { $ref: "#/components/schemas/ContainerResources" } + ToolWasmSpec: + type: object + properties: + module: { type: string, description: "Local path, HTTPS URL, or OCI reference (oci://...) to the .wasm module" } + entrypoint: { type: string, description: "Exported function name to call (default: run)" } + max_memory_bytes: { type: integer, format: int64, description: "Maximum memory in bytes (default: 64MB)" } + fuel: { type: integer, format: uint64, description: "Maximum fuel units for execution metering" } + enable_wasi: { type: boolean, description: "Enable WASI capabilities (stdin/stdout/stderr)" } + image_pull_secret: { type: string, description: "Name of a Secret containing registry credentials for pulling OCI-referenced WASM modules." } + ToolSpec: + type: object + properties: + type: { type: string, enum: [http, external, grpc, webhook-callback, mcp, wasm, cli, a2a] } + endpoint: { type: string } + description: { type: string } + input_schema: { type: object, additionalProperties: true } + mcp_server_ref: { type: string } + mcp_tool_name: { type: string } + cli: { $ref: "#/components/schemas/ToolCliSpec" } + wasm: { $ref: "#/components/schemas/ToolWasmSpec" } + capabilities: + type: array + items: { type: string } + operation_classes: + type: array + items: { type: string } + risk_level: { type: string } + runtime: { $ref: "#/components/schemas/ToolRuntimePolicy" } + auth: { $ref: "#/components/schemas/ToolAuth" } + a2a: + $ref: "#/components/schemas/ToolA2ASpec" + ToolA2ASpec: + type: object + properties: + agent_url: { type: string, format: uri, description: "Remote A2A agent endpoint URL or base URL" } + protocol_version: { type: string, description: "A2A protocol version (defaults to server-configured version)" } + prefer_streaming: { type: boolean, description: "Prefer streaming (sendSubscribe) when the remote agent supports it" } + ToolStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + observedGeneration: { type: integer, format: int64 } + Tool: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Tool] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/ToolSpec" } + status: { $ref: "#/components/schemas/ToolStatus" } + ToolList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Tool" } diff --git a/openapi/schemas/worker.yaml b/openapi/schemas/worker.yaml new file mode 100644 index 0000000..e0cb193 --- /dev/null +++ b/openapi/schemas/worker.yaml @@ -0,0 +1,41 @@ +components: + schemas: + WorkerCapabilities: + type: object + properties: + gpu: { type: boolean } + supported_models: + type: array + items: { type: string } + WorkerSpec: + type: object + properties: + region: { type: string } + capabilities: { $ref: "#/components/schemas/WorkerCapabilities" } + max_concurrent_tasks: { type: integer } + WorkerStatus: + type: object + properties: + phase: { type: string } + lastError: { type: string } + lastHeartbeat: { type: string } + observedGeneration: { type: integer, format: int64 } + currentTasks: { type: integer } + Worker: + type: object + required: [apiVersion, kind, metadata, spec] + properties: + apiVersion: { type: string, enum: [orloj.dev/v1] } + kind: { type: string, enum: [Worker] } + metadata: { $ref: "common.yaml#/components/schemas/ObjectMeta" } + spec: { $ref: "#/components/schemas/WorkerSpec" } + status: { $ref: "#/components/schemas/WorkerStatus" } + WorkerList: + allOf: + - { $ref: "common.yaml#/components/schemas/ListMeta" } + - type: object + required: [items] + properties: + items: + type: array + items: { $ref: "#/components/schemas/Worker" } diff --git a/pyproject.toml b/pyproject.toml index 4a50279..756f44d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ Documentation = "https://docs.orloj.dev" Repository = "https://github.com/OrlojHQ/orloj-python-sdk" Issues = "https://github.com/OrlojHQ/orloj-python-sdk/issues" -[project.optional-dependencies] +[dependency-groups] dev = [ "pytest>=8", "pytest-asyncio>=0.23", @@ -38,6 +38,8 @@ dev = [ "ruff>=0.3", "mypy>=1.9", "coverage>=7", + "datamodel-code-generator>=0.26,<1", + "PyYAML>=6", ] [tool.hatch.build.targets.wheel] @@ -51,6 +53,7 @@ fallback-version = "0.1.0" [tool.ruff] target-version = "py310" line-length = 100 +extend-exclude = ["src/orloj_sdk/models/_generated"] [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "SIM", "TC"] @@ -60,6 +63,11 @@ ignore = ["TC001", "TC002", "TC003"] python_version = "3.10" strict = true plugins = ["pydantic.mypy"] +exclude = ["src/orloj_sdk/models/_generated/"] + +[[tool.mypy.overrides]] +module = ["orloj_sdk.models._generated.*"] +ignore_errors = true [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/scripts/bundle_openapi.py b/scripts/bundle_openapi.py new file mode 100644 index 0000000..5f77295 --- /dev/null +++ b/scripts/bundle_openapi.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Bundle openapi/openapi.yaml + schemas/*.yaml into a single self-contained YAML. + +Used as input for datamodel-code-generator (which does not follow external $refs). +""" + +from __future__ import annotations + +import copy +import sys +from pathlib import Path +from typing import Any + +import yaml + + +def load_yaml(path: Path) -> Any: + return yaml.safe_load(path.read_text(encoding="utf-8")) + + +def merge_schema_components(schemas_dir: Path) -> dict[str, Any]: + merged: dict[str, Any] = {} + for path in sorted(schemas_dir.glob("*.yaml")): + data = load_yaml(path) + comps = (data or {}).get("components") or {} + schemas = comps.get("schemas") or {} + for name, schema in schemas.items(): + if name in merged and merged[name] != schema: + # Last write wins but warn — schemas should be unique by name. + print(f"warning: duplicate schema component {name!r} from {path.name}", file=sys.stderr) + merged[name] = copy.deepcopy(schema) + return merged + + +def rewrite_refs(obj: Any) -> Any: + """Rewrite ./schemas/foo.yaml#/components/schemas/X → #/components/schemas/X.""" + if isinstance(obj, dict): + out: dict[str, Any] = {} + for key, val in obj.items(): + if key == "$ref" and isinstance(val, str): + if "#/components/schemas/" in val: + name = val.split("#/components/schemas/", 1)[1] + out[key] = f"#/components/schemas/{name}" + else: + out[key] = val + else: + out[key] = rewrite_refs(val) + return out + if isinstance(obj, list): + return [rewrite_refs(x) for x in obj] + return obj + + +def bundle(repo_openapi_dir: Path) -> dict[str, Any]: + root = load_yaml(repo_openapi_dir / "openapi.yaml") + if not isinstance(root, dict): + raise SystemExit("openapi.yaml did not parse to a mapping") + schemas = merge_schema_components(repo_openapi_dir / "schemas") + components = root.setdefault("components", {}) + existing = components.get("schemas") or {} + # Root-level shared components (if any) plus schema files. + combined = {**schemas, **existing} + components["schemas"] = combined + return rewrite_refs(root) # type: ignore[no-any-return] + + +def main() -> int: + repo_root = Path(__file__).resolve().parents[1] + openapi_dir = repo_root / "openapi" + out = openapi_dir / "openapi.bundle.yaml" + data = bundle(openapi_dir) + # Prefer block style for readability; still fine for codegen. + out.write_text( + yaml.safe_dump(data, sort_keys=False, allow_unicode=True), + encoding="utf-8", + ) + n = len((data.get("components") or {}).get("schemas") or {}) + print(f"wrote {out.relative_to(repo_root)} with {n} component schemas") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_schema_parity.py b/scripts/check_schema_parity.py new file mode 100644 index 0000000..d3f7846 --- /dev/null +++ b/scripts/check_schema_parity.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Fail if hand-written SDK models lag the vendored OpenAPI schemas. + +Compares property names (normalized to snake_case) from openapi/schemas/*.yaml +against public fields on orloj_sdk.models.* OrlojModel subclasses. +""" + +from __future__ import annotations + +import ast +import re +import sys +from pathlib import Path + +try: + import yaml +except ImportError: + print("error: PyYAML required (dev dependency)", file=sys.stderr) + raise SystemExit(1) + +# OpenAPI schema file -> SDK model module (relative to models/) +SCHEMA_TO_MODULE: dict[str, str] = { + "agent.yaml": "agent.py", + "agent-system.yaml": "agent_system.py", + "agent-policy.yaml": "agent_policy.py", + "agent-role.yaml": "agent_role.py", + "task.yaml": "task.py", + "task-schedule.yaml": "task_schedule.py", + "task-webhook.yaml": "task_webhook.py", + "task-approval.yaml": "task_approval.py", + "tool.yaml": "tool.py", + "tool-approval.yaml": "tool_approval.py", + "tool-permission.yaml": "tool_permission.py", + "memory.yaml": "memory.py", + "model-endpoint.yaml": "model_endpoint.py", + "secret.yaml": "secret.py", + "sealed-secret.yaml": "sealed_secret.py", + "mcp-server.yaml": "mcp_server.py", + "worker.yaml": "worker.py", + "context-adapter.yaml": "context_adapter.py", + "eval-dataset.yaml": "eval.py", + "eval-run.yaml": "eval.py", + "a2a.yaml": "a2a.py", +} + +# Schema properties we intentionally do not require on every SDK model type +# (wrapper/list bookkeeping, or duplicated across shared common schemas). +NOISE = frozenset( + { + "api_version", + "kind", + "metadata", + "spec", + "status", + "items", + "type", + "additional_properties", + } +) + +# Known intentional gaps (document when adding). Empty = strict full coverage. +ALLOW_MISSING: dict[str, frozenset[str]] = { + # YAML schema-only enums / infra not yet exposed as first-class SDK DTOs: + # leave empty — fail closed so new OpenAPI fields force SDK updates. +} + + +def norm(name: str) -> str: + s = re.sub(r"([A-Z])", r"_\1", name).lower().strip("_") + return s.replace("-", "_") + + +def walk_openapi_props(obj: object, acc: set[str]) -> None: + if isinstance(obj, dict): + props = obj.get("properties") + if isinstance(props, dict): + for key, val in props.items(): + acc.add(norm(str(key))) + walk_openapi_props(val, acc) + for val in obj.values(): + walk_openapi_props(val, acc) + elif isinstance(obj, list): + for val in obj: + walk_openapi_props(val, acc) + + +def collect_openapi_props(schema_path: Path) -> set[str]: + data = yaml.safe_load(schema_path.read_text(encoding="utf-8")) + acc: set[str] = set() + walk_openapi_props(data, acc) + return acc + + +def collect_py_fields(module_path: Path) -> set[str]: + """Parse Pydantic field names and Field(..., alias=) from a module AST.""" + tree = ast.parse(module_path.read_text(encoding="utf-8"), filename=str(module_path)) + fields: set[str] = set() + for node in ast.walk(tree): + if not isinstance(node, ast.AnnAssign) or not isinstance(node.target, ast.Name): + continue + name = node.target.id + if name in {"model_config", "ClassVar"}: + continue + fields.add(norm(name)) + if isinstance(node.value, ast.Call): + for kw in node.value.keywords: + if kw.arg == "alias" and isinstance(kw.value, ast.Constant): + if isinstance(kw.value.value, str): + fields.add(norm(kw.value.value)) + return fields + + +def main() -> int: + repo_root = Path(__file__).resolve().parents[1] + schemas_dir = repo_root / "openapi" / "schemas" + models_dir = repo_root / "src" / "orloj_sdk" / "models" + failures: list[str] = [] + + for schema_name, module_name in sorted(SCHEMA_TO_MODULE.items()): + schema_path = schemas_dir / schema_name + module_path = models_dir / module_name + if not schema_path.is_file(): + failures.append(f"missing schema file: {schema_name}") + continue + if not module_path.is_file(): + failures.append(f"missing SDK model module for {schema_name}: {module_name}") + continue + oa = collect_openapi_props(schema_path) - NOISE + py = collect_py_fields(module_path) + allow = ALLOW_MISSING.get(schema_name, frozenset()) + missing = sorted(oa - py - allow) + if missing: + sample = ", ".join(missing[:20]) + more = f" (+{len(missing) - 20} more)" if len(missing) > 20 else "" + failures.append(f"{schema_name} → {module_name}: missing fields: {sample}{more}") + + if failures: + print("Schema parity check FAILED — hand-written models lag OpenAPI:\n", file=sys.stderr) + for line in failures: + print(f" - {line}", file=sys.stderr) + print( + "\nUpdate the SDK models, or sync OpenAPI with:\n" + " python scripts/sync_openapi.py --orloj-root /path/to/orloj\n" + " python scripts/generate_models.py\n", + file=sys.stderr, + ) + return 1 + + print(f"Schema parity OK ({len(SCHEMA_TO_MODULE)} schema files checked).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_models.py b/scripts/generate_models.py new file mode 100644 index 0000000..bf13137 --- /dev/null +++ b/scripts/generate_models.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Generate Pydantic models from the vendored OpenAPI into models/_generated/. + +The public orloj_sdk.models package remains hand-written for DX/aliases. +CI regenerates this tree and fails if the checkout is stale, so the OpenAPI +pin cannot drift from committed generated artifacts. +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +from pathlib import Path + + +HEADER = '''\ +"""AUTO-GENERATED from vendored OpenAPI. Do not edit by hand. + +Regenerate: python scripts/generate_models.py +Source pin: openapi/PIN.json +""" +''' + + +def main() -> int: + repo_root = Path(__file__).resolve().parents[1] + scripts_dir = Path(__file__).resolve().parent + sys.path.insert(0, str(scripts_dir)) + + from bundle_openapi import bundle # noqa: PLC0415 + + import yaml # noqa: PLC0415 + + openapi_dir = repo_root / "openapi" + if not (openapi_dir / "openapi.yaml").is_file(): + print(f"error: missing {openapi_dir / 'openapi.yaml'}", file=sys.stderr) + return 1 + + bundled = openapi_dir / "openapi.bundle.yaml" + data = bundle(openapi_dir) + bundled.write_text( + yaml.safe_dump(data, sort_keys=False, allow_unicode=True), + encoding="utf-8", + ) + n = len((data.get("components") or {}).get("schemas") or {}) + print(f"bundled {n} schemas → {bundled.relative_to(repo_root)}") + + out_dir = repo_root / "src" / "orloj_sdk" / "models" / "_generated" + out_dir.mkdir(parents=True, exist_ok=True) + for path in list(out_dir.iterdir()): + if path.is_file(): + path.unlink() + elif path.is_dir(): + shutil.rmtree(path) + + models_py = out_dir / "models.py" + cmd = [ + sys.executable, + "-m", + "datamodel_code_generator", + "--input", + str(bundled), + "--input-file-type", + "openapi", + "--output", + str(models_py), + "--output-model-type", + "pydantic_v2.BaseModel", + "--use-standard-collections", + "--use-union-operator", + "--target-python-version", + "3.10", + "--collapse-root-models", + "--field-constraints", + "--reuse-model", + "--strict-nullable", + "--use-title-as-name", + "--formatters", + "builtin", + ] + print("running:", " ".join(cmd)) + try: + subprocess.check_call(cmd, cwd=repo_root) + except subprocess.CalledProcessError as exc: + return exc.returncode + + if not models_py.is_file() or models_py.stat().st_size < 100: + print("error: generator produced empty output", file=sys.stderr) + return 1 + + text = models_py.read_text(encoding="utf-8") + text = re.sub( + r"^#\s+generated by.*$", + "# generated by datamodel-code-generator (see scripts/generate_models.py)", + text, + count=1, + flags=re.M | re.I, + ) + text = re.sub(r"^#\s+timestamp:.*\n", "", text, flags=re.M | re.I) + if "AUTO-GENERATED" not in text[:300]: + text = HEADER + "\n" + text + models_py.write_text(text, encoding="utf-8") + + (out_dir / "__init__.py").write_text( + '"""Generated OpenAPI models (reference + CI freshness gate)."""\n' + "from orloj_sdk.models._generated.models import * # noqa: F403\n", + encoding="utf-8", + ) + print(f"wrote {models_py.relative_to(repo_root)} ({models_py.stat().st_size} bytes)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/sync_openapi.py b/scripts/sync_openapi.py new file mode 100644 index 0000000..797ef20 --- /dev/null +++ b/scripts/sync_openapi.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Sync vendored OpenAPI from a local orloj checkout and update PIN.json.""" + +from __future__ import annotations + +import argparse +import json +import shutil +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + + +def _git(orloj_root: Path, *args: str) -> str: + return subprocess.check_output(["git", *args], cwd=orloj_root, text=True).strip() + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--orloj-root", + type=Path, + required=True, + help="Path to a local OrlojHQ/orloj checkout", + ) + args = parser.parse_args() + orloj_root: Path = args.orloj_root.resolve() + src = orloj_root / "openapi" + if not (src / "openapi.yaml").is_file(): + print(f"error: missing {src / 'openapi.yaml'}", file=sys.stderr) + return 1 + + repo_root = Path(__file__).resolve().parents[1] + dst = repo_root / "openapi" + + # Replace schemas and root spec (keep PIN until rewritten). + schemas_dst = dst / "schemas" + if schemas_dst.exists(): + shutil.rmtree(schemas_dst) + shutil.copytree(src / "schemas", schemas_dst) + shutil.copy2(src / "openapi.yaml", dst / "openapi.yaml") + build_py = src / "build_openapi.py" + if build_py.is_file(): + shutil.copy2(build_py, dst / "build_openapi.py") + + commit = _git(orloj_root, "rev-parse", "HEAD") + try: + tag = _git(orloj_root, "describe", "--tags", "--always") + except subprocess.CalledProcessError: + tag = commit[:12] + + pin = { + "repository": "https://github.com/OrlojHQ/orloj", + "commit": commit, + "tag": tag, + "synced_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "note": "Vendored OpenAPI from orloj. Update via: python scripts/sync_openapi.py --orloj-root /path/to/orloj", + } + (dst / "PIN.json").write_text(json.dumps(pin, indent=2) + "\n", encoding="utf-8") + print(f"synced OpenAPI from {orloj_root} @ {tag} ({commit[:12]})") + print("next: python scripts/generate_models.py && python scripts/check_schema_parity.py") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/orloj_sdk/__init__.py b/src/orloj_sdk/__init__.py index 06df05d..6d8601f 100644 --- a/src/orloj_sdk/__init__.py +++ b/src/orloj_sdk/__init__.py @@ -11,6 +11,7 @@ RateLimitError, ServiceUnavailableError, ) +from orloj_sdk._tasks import TaskFailedError, TaskTimeoutError, parse_task_output from orloj_sdk._version import __version__ from orloj_sdk.client import AsyncOrlojClient, OrlojClient @@ -28,5 +29,8 @@ "OrlojTimeoutError", "RateLimitError", "ServiceUnavailableError", + "TaskFailedError", + "TaskTimeoutError", + "parse_task_output", "__version__", ] diff --git a/src/orloj_sdk/_tasks.py b/src/orloj_sdk/_tasks.py new file mode 100644 index 0000000..bcfde14 --- /dev/null +++ b/src/orloj_sdk/_tasks.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import TypeVar + +from pydantic import BaseModel + +from orloj_sdk._errors import OrlojError +from orloj_sdk.models._base import ObjectMeta +from orloj_sdk.models.task import Task, TaskSpec + +T = TypeVar("T", bound=BaseModel) + +TERMINAL_TASK_PHASES: frozenset[str] = frozenset({"Succeeded", "Failed", "DeadLetter"}) +SUCCESS_TASK_PHASES: frozenset[str] = frozenset({"Succeeded"}) +FAILURE_TASK_PHASES: frozenset[str] = frozenset({"Failed", "DeadLetter"}) + +DEFAULT_RUN_TIMEOUT_SECONDS = 300.0 +DEFAULT_RUN_POLL_SECONDS = 2.0 + + +def is_terminal_phase(phase: str | None) -> bool: + return phase in TERMINAL_TASK_PHASES + + +def is_success_phase(phase: str | None) -> bool: + return phase in SUCCESS_TASK_PHASES + + +class TaskFailedError(OrlojError): + """Raised when a waited task ends in Failed or DeadLetter.""" + + def __init__(self, task: Task) -> None: + phase = task.status.phase if task.status else None + err = task.status.last_error if task.status else None + msg = f"task {task.metadata.name!r} ended in phase {phase!r}" + if err: + msg = f"{msg}: {err}" + super().__init__(msg) + self.task = task + self.phase = phase + + +class TaskTimeoutError(OrlojError): + """Raised when wait=True exceeds the configured timeout.""" + + def __init__(self, name: str, timeout: float, *, task: Task | None = None) -> None: + super().__init__(f"timed out waiting for task {name!r} after {timeout:g}s") + self.task = task + self.timeout = timeout + + +def parse_task_output(task: Task, model_type: type[T], *, key: str | None = None) -> T: + """Parse ``task.status.output`` into a Pydantic model. + + Orloj stores task output as ``dict[str, str]``. By default this helper: + + 1. If ``key`` is set, parse that string value as JSON into ``model_type``. + 2. Else if a single output value looks like JSON object/array, parse it. + 3. Else treat the whole output map as the model input (string fields). + """ + output = (task.status.output if task.status else None) or {} + if key is not None: + if key not in output: + raise KeyError(f"task output missing key {key!r}; have {sorted(output)}") + return model_type.model_validate_json(output[key]) + + if len(output) == 1: + only = next(iter(output.values())) + stripped = only.strip() + if stripped.startswith("{") or stripped.startswith("["): + return model_type.model_validate_json(only) + + return model_type.model_validate(output) + + +def coerce_task_body( + body: Task | None, + *, + name: str | None, + system: str | None, + input: dict[str, str] | None, + namespace: str, +) -> Task: + """Build a Task create body from either a full Task or shorthand kwargs.""" + if body is not None: + return body + if not name: + raise ValueError("tasks.run requires either a Task body or name=") + if not system: + raise ValueError("tasks.run requires system= when creating from shorthand kwargs") + return Task( + metadata=ObjectMeta(name=name, namespace=namespace), + spec=TaskSpec(system=system, input=input), + ) + + +def task_phase(task: Task) -> str | None: + return task.status.phase if task.status else None diff --git a/src/orloj_sdk/authoring.py b/src/orloj_sdk/authoring.py new file mode 100644 index 0000000..93c4b70 --- /dev/null +++ b/src/orloj_sdk/authoring.py @@ -0,0 +1,233 @@ +"""Graph-as-code authoring helpers that emit Orloj resources. + +This layer sits on top of the typed REST client. Decorators and builders +collect Agent / AgentSystem definitions in memory; call ``apply(client)`` +(or ``SystemBuilder.apply``) to create them via the API. +""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field +from typing import Any, TypeVar + +from orloj_sdk.client import AsyncOrlojClient, OrlojClient +from orloj_sdk.models._base import ObjectMeta +from orloj_sdk.models.agent import Agent, AgentExecutionSpec, AgentLimits, AgentSpec, MemorySpec +from orloj_sdk.models.agent_system import ( + AgentSystem, + AgentSystemA2ASpec, + AgentSystemSpec, + EdgeCondition, + GraphEdge, + GraphJoin, + GraphRoute, + ReviewCheckpointSpec, +) + +F = TypeVar("F", bound=Callable[..., Any]) + + +@dataclass +class AgentBuilder: + """Fluent builder for an Agent resource.""" + + name: str + namespace: str = "default" + model_ref: str | None = None + prompt: str | None = None + tools: list[str] = field(default_factory=list) + allowed_tools: list[str] | None = None + roles: list[str] | None = None + fallback_model_refs: list[str] | None = None + memory: MemorySpec | None = None + execution: AgentExecutionSpec | None = None + limits: AgentLimits | None = None + labels: dict[str, str] | None = None + + def with_prompt(self, prompt: str) -> AgentBuilder: + self.prompt = prompt + return self + + def with_model(self, model_ref: str, *fallbacks: str) -> AgentBuilder: + self.model_ref = model_ref + if fallbacks: + self.fallback_model_refs = list(fallbacks) + return self + + def with_tools(self, *tools: str) -> AgentBuilder: + self.tools = list(tools) + return self + + def with_limits( + self, + *, + max_steps: int | None = None, + timeout: str | None = None, + ) -> AgentBuilder: + self.limits = AgentLimits(max_steps=max_steps, timeout=timeout) + return self + + def with_output_schema(self, schema: dict[str, Any]) -> AgentBuilder: + execution = self.execution or AgentExecutionSpec() + execution.output_schema = schema + self.execution = execution + return self + + def to_resource(self) -> Agent: + return Agent( + metadata=ObjectMeta( + name=self.name, + namespace=self.namespace, + labels=self.labels, + ), + spec=AgentSpec( + model_ref=self.model_ref, + fallback_model_refs=self.fallback_model_refs, + prompt=self.prompt, + tools=self.tools or None, + allowed_tools=self.allowed_tools, + roles=self.roles, + memory=self.memory, + execution=self.execution, + limits=self.limits, + ), + ) + + +def agent( + name: str, + *, + model_ref: str | None = None, + prompt: str | None = None, + tools: list[str] | None = None, + namespace: str = "default", +) -> Callable[[F], AgentBuilder]: + """Decorator that turns a function docstring/body into an AgentBuilder. + + The decorated function is not invoked at apply time; use it as a + documentation anchor. Prefer returning/using the builder directly:: + + @agent("researcher", model_ref="claude", tools=["web_search"]) + def researcher() -> None: + '''Research the topic and summarize findings.''' + + # researcher is an AgentBuilder + client.agents.create(researcher.to_resource()) + """ + + def decorator(fn: F) -> AgentBuilder: + builder = AgentBuilder( + name=name, + namespace=namespace, + model_ref=model_ref, + prompt=prompt or (fn.__doc__.strip() if fn.__doc__ else None), + tools=list(tools or []), + ) + return builder + + return decorator + + +def edge( + to: str, + *, + labels: dict[str, str] | None = None, + condition: EdgeCondition | None = None, +) -> GraphRoute: + return GraphRoute(to=to, labels=labels, condition=condition) + + +def node( + *, + next: str | None = None, + edges: list[GraphRoute] | None = None, + join: GraphJoin | None = None, + delegates: list[GraphRoute] | None = None, + delegate_join: GraphJoin | None = None, + review: ReviewCheckpointSpec | None = None, +) -> GraphEdge: + return GraphEdge( + next=next, + edges=edges, + join=join, + delegates=delegates, + delegate_join=delegate_join, + review=review, + ) + + +@dataclass +class SystemBuilder: + """Fluent builder for an AgentSystem + optional Agent resources.""" + + name: str + namespace: str = "default" + agents: list[AgentBuilder] = field(default_factory=list) + graph: dict[str, GraphEdge] = field(default_factory=dict) + context_adapter: str | None = None + a2a: AgentSystemA2ASpec | None = None + completion_review: ReviewCheckpointSpec | None = None + labels: dict[str, str] | None = None + + def add_agent(self, builder: AgentBuilder) -> SystemBuilder: + self.agents.append(builder) + if builder.name not in self.graph: + self.graph[builder.name] = GraphEdge() + return self + + def set_graph(self, graph: Mapping[str, GraphEdge]) -> SystemBuilder: + self.graph = dict(graph) + return self + + def enable_a2a(self, *, auth: str = "bearer") -> SystemBuilder: + self.a2a = AgentSystemA2ASpec(enabled=True, auth=auth) + return self + + def to_system_resource(self) -> AgentSystem: + agent_names = [a.name for a in self.agents] or list(self.graph.keys()) + return AgentSystem( + metadata=ObjectMeta( + name=self.name, + namespace=self.namespace, + labels=self.labels, + ), + spec=AgentSystemSpec( + agents=agent_names, + graph=self.graph or None, + context_adapter=self.context_adapter, + a2a=self.a2a, + completion_review=self.completion_review, + ), + ) + + def to_agent_resources(self) -> list[Agent]: + return [a.to_resource() for a in self.agents] + + def apply(self, client: OrlojClient) -> AgentSystem: + """Create agents then the agent system via the REST client.""" + for agent_res in self.to_agent_resources(): + client.agents.create(agent_res) + return client.agent_systems.create(self.to_system_resource()) + + async def apply_async(self, client: AsyncOrlojClient) -> AgentSystem: + for agent_res in self.to_agent_resources(): + await client.agents.create(agent_res) + return await client.agent_systems.create(self.to_system_resource()) + + +def system(name: str, *, namespace: str = "default") -> SystemBuilder: + return SystemBuilder(name=name, namespace=namespace) + + +def apply(client: OrlojClient, *builders: AgentBuilder | SystemBuilder) -> list[Any]: + """Apply one or more builders against a sync client.""" + created: list[Any] = [] + for builder in builders: + if isinstance(builder, SystemBuilder): + created.append(builder.apply(client)) + elif isinstance(builder, AgentBuilder): + created.append(client.agents.create(builder.to_resource())) + else: + raise TypeError(f"unsupported builder type: {type(builder)!r}") + return created diff --git a/src/orloj_sdk/client.py b/src/orloj_sdk/client.py index 8ff1217..7d9390b 100644 --- a/src/orloj_sdk/client.py +++ b/src/orloj_sdk/client.py @@ -21,16 +21,22 @@ should_retry_status, sync_request_with_retries, ) +from orloj_sdk.resources.a2a import A2A, AsyncA2A from orloj_sdk.resources.agent_policies import AgentPolicies, AsyncAgentPolicies from orloj_sdk.resources.agent_roles import AgentRoles, AsyncAgentRoles from orloj_sdk.resources.agent_systems import AgentSystems, AsyncAgentSystems from orloj_sdk.resources.agents import Agents, AsyncAgents from orloj_sdk.resources.auth import AsyncAuth, Auth +from orloj_sdk.resources.context_adapters import AsyncContextAdapters, ContextAdapters +from orloj_sdk.resources.eval_datasets import AsyncEvalDatasets, EvalDatasets +from orloj_sdk.resources.eval_runs import AsyncEvalRuns, EvalRuns from orloj_sdk.resources.events import AsyncEvents, Events from orloj_sdk.resources.mcp_servers import AsyncMcpServers, McpServers from orloj_sdk.resources.memories import AsyncMemories, Memories from orloj_sdk.resources.model_endpoints import AsyncModelEndpoints, ModelEndpoints +from orloj_sdk.resources.sealed_secrets import AsyncSealedSecrets, SealedSecrets from orloj_sdk.resources.secrets import AsyncSecrets, Secrets +from orloj_sdk.resources.task_approvals import AsyncTaskApprovals, TaskApprovals from orloj_sdk.resources.task_schedules import AsyncTaskSchedules, TaskSchedules from orloj_sdk.resources.task_webhooks import AsyncTaskWebhooks, TaskWebhooks from orloj_sdk.resources.tasks import AsyncTasks, Tasks @@ -64,7 +70,7 @@ def __init__( api_token: str | None = None, timeout: float | httpx.Timeout | None = None, namespace: str = "default", - max_retries: int = 0, + max_retries: int = 3, ) -> None: self._base_url = resolve_base_url(base_url) self._token = resolve_api_token(api_token) @@ -82,11 +88,16 @@ def __init__( self.model_endpoints = ModelEndpoints(self) self.tools = Tools(self) self.secrets = Secrets(self) + self.sealed_secrets = SealedSecrets(self) self.memories = Memories(self) + self.context_adapters = ContextAdapters(self) + self.eval_datasets = EvalDatasets(self) + self.eval_runs = EvalRuns(self) self.agent_policies = AgentPolicies(self) self.agent_roles = AgentRoles(self) self.tool_permissions = ToolPermissions(self) self.tool_approvals = ToolApprovals(self) + self.task_approvals = TaskApprovals(self) self.tasks = Tasks(self) self.task_schedules = TaskSchedules(self) self.task_webhooks = TaskWebhooks(self) @@ -94,6 +105,7 @@ def __init__( self.mcp_servers = McpServers(self) self.auth = Auth(self) self.events = Events(self) + self.a2a = A2A(self) def _merge_headers(self, extra: dict[str, str] | None) -> dict[str, str]: if not extra: @@ -197,7 +209,7 @@ def __init__( api_token: str | None = None, timeout: float | httpx.Timeout | None = None, namespace: str = "default", - max_retries: int = 0, + max_retries: int = 3, ) -> None: self._base_url = resolve_base_url(base_url) self._token = resolve_api_token(api_token) @@ -215,11 +227,16 @@ def __init__( self.model_endpoints = AsyncModelEndpoints(self) self.tools = AsyncTools(self) self.secrets = AsyncSecrets(self) + self.sealed_secrets = AsyncSealedSecrets(self) self.memories = AsyncMemories(self) + self.context_adapters = AsyncContextAdapters(self) + self.eval_datasets = AsyncEvalDatasets(self) + self.eval_runs = AsyncEvalRuns(self) self.agent_policies = AsyncAgentPolicies(self) self.agent_roles = AsyncAgentRoles(self) self.tool_permissions = AsyncToolPermissions(self) self.tool_approvals = AsyncToolApprovals(self) + self.task_approvals = AsyncTaskApprovals(self) self.tasks = AsyncTasks(self) self.task_schedules = AsyncTaskSchedules(self) self.task_webhooks = AsyncTaskWebhooks(self) @@ -227,6 +244,7 @@ def __init__( self.mcp_servers = AsyncMcpServers(self) self.auth = AsyncAuth(self) self.events = AsyncEvents(self) + self.a2a = AsyncA2A(self) def _merge_headers(self, extra: dict[str, str] | None) -> dict[str, str]: if not extra: diff --git a/src/orloj_sdk/models/__init__.py b/src/orloj_sdk/models/__init__.py index 09dedae..bf686c5 100644 --- a/src/orloj_sdk/models/__init__.py +++ b/src/orloj_sdk/models/__init__.py @@ -1,4 +1,21 @@ from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel, StatusEnvelope, dump_json_body +from orloj_sdk.models.a2a import ( + A2AArtifact, + A2AJsonRpcError, + A2AJsonRpcRequest, + A2AJsonRpcResponse, + A2AMessage, + A2APart, + A2ARegistryResponse, + A2AStatus, + A2ATask, + AgentCard, + AgentCardAuthentication, + AgentCardProvider, + CardCapabilities, + CardSkill, + RemoteAgentEntry, +) from orloj_sdk.models.agent import ( Agent, AgentExecutionSpec, @@ -17,14 +34,45 @@ from orloj_sdk.models.agent_role import AgentRole, AgentRoleList, AgentRoleSpec, AgentRoleStatus from orloj_sdk.models.agent_system import ( AgentSystem, + AgentSystemA2ASpec, AgentSystemList, AgentSystemSpec, AgentSystemStatus, + EdgeCondition, GraphEdge, GraphJoin, GraphRoute, + ReviewCheckpointSpec, ) from orloj_sdk.models.auth import NamedLogsResponse, TokenInfo, WhoAmI +from orloj_sdk.models.context_adapter import ( + ContextAdapter, + ContextAdapterList, + ContextAdapterSpec, + ContextAdapterStatus, +) +from orloj_sdk.models.eval import ( + AgentOverride, + EvalBulkImportBody, + EvalDataset, + EvalDatasetList, + EvalDatasetSpec, + EvalDatasetStatus, + EvalExpected, + EvalRun, + EvalRunCompareResponse, + EvalRunCompareSample, + EvalRunCompareSampleResult, + EvalRunCompareSummary, + EvalRunList, + EvalRunSpec, + EvalRunStatus, + EvalSample, + EvalSampleAnnotation, + EvalSampleResult, + EvalScoringConfig, + EvalSummary, +) from orloj_sdk.models.event import ServerEvent, WatchEvent from orloj_sdk.models.mcp_server import ( McpReconnectPolicy, @@ -51,14 +99,34 @@ ModelEndpointSpec, ModelEndpointStatus, ) +from orloj_sdk.models.observability import ( + CLITokenResponse, + TaskMessageAgentMetric, + TaskMessageEdgeMetric, + TaskMessageListResponse, + TaskMessageMetricsResponse, + TaskMessageTotals, +) +from orloj_sdk.models.sealed_secret import ( + SealedSecret, + SealedSecretList, + SealedSecretPublicKeyResponse, + SealedSecretSpec, + SealedSecretStatus, + SealedSecretTemplateSecret, + SealedValue, +) from orloj_sdk.models.secret import Secret, SecretList, SecretSpec, SecretStatus from orloj_sdk.models.task import ( Task, + TaskBlockedOn, + TaskDelegationState, TaskHistoryEvent, TaskJoinSource, TaskJoinState, TaskList, TaskMessage, + TaskMessageIdempotency, TaskMessageRetryPolicy, TaskRequirements, TaskRetryPolicy, @@ -66,6 +134,12 @@ TaskStatus, TaskTraceEvent, ) +from orloj_sdk.models.task_approval import ( + TaskApproval, + TaskApprovalList, + TaskApprovalSpec, + TaskApprovalStatus, +) from orloj_sdk.models.task_schedule import ( TaskSchedule, TaskScheduleList, @@ -82,7 +156,9 @@ TaskWebhookStatus, ) from orloj_sdk.models.tool import ( + ContainerResources, Tool, + ToolA2ASpec, ToolAuth, ToolCliEnvRef, ToolCliSpec, @@ -91,6 +167,7 @@ ToolRuntimePolicy, ToolSpec, ToolStatus, + ToolWasmSpec, ) from orloj_sdk.models.tool_approval import ( ToolApproval, @@ -108,10 +185,23 @@ from orloj_sdk.models.worker import Worker, WorkerCapabilities, WorkerList, WorkerSpec, WorkerStatus __all__ = [ + "A2AArtifact", + "A2AJsonRpcError", + "A2AJsonRpcRequest", + "A2AJsonRpcResponse", + "A2AMessage", + "A2APart", + "A2ARegistryResponse", + "A2AStatus", + "A2ATask", "Agent", + "AgentCard", + "AgentCardAuthentication", + "AgentCardProvider", "AgentExecutionSpec", "AgentLimits", "AgentList", + "AgentOverride", "AgentPolicy", "AgentPolicyList", "AgentPolicySpec", @@ -122,9 +212,38 @@ "AgentSpec", "AgentStatus", "AgentSystem", + "AgentSystemA2ASpec", "AgentSystemList", "AgentSystemSpec", "AgentSystemStatus", + "CLITokenResponse", + "CardCapabilities", + "CardSkill", + "ContainerResources", + "ContextAdapter", + "ContextAdapterList", + "ContextAdapterSpec", + "ContextAdapterStatus", + "EdgeCondition", + "EvalBulkImportBody", + "EvalDataset", + "EvalDatasetList", + "EvalDatasetSpec", + "EvalDatasetStatus", + "EvalExpected", + "EvalRun", + "EvalRunCompareResponse", + "EvalRunCompareSample", + "EvalRunCompareSampleResult", + "EvalRunCompareSummary", + "EvalRunList", + "EvalRunSpec", + "EvalRunStatus", + "EvalSample", + "EvalSampleAnnotation", + "EvalSampleResult", + "EvalScoringConfig", + "EvalSummary", "GraphEdge", "GraphJoin", "GraphRoute", @@ -154,6 +273,15 @@ "OperationRule", "OrlojModel", "PolicyStatus", + "RemoteAgentEntry", + "ReviewCheckpointSpec", + "SealedSecret", + "SealedSecretList", + "SealedSecretPublicKeyResponse", + "SealedSecretSpec", + "SealedSecretStatus", + "SealedSecretTemplateSecret", + "SealedValue", "Secret", "SecretList", "SecretSpec", @@ -161,12 +289,24 @@ "ServerEvent", "StatusEnvelope", "Task", + "TaskApproval", + "TaskApprovalList", + "TaskApprovalSpec", + "TaskApprovalStatus", + "TaskBlockedOn", + "TaskDelegationState", "TaskHistoryEvent", "TaskJoinSource", "TaskJoinState", "TaskList", "TaskMessage", + "TaskMessageAgentMetric", + "TaskMessageEdgeMetric", + "TaskMessageIdempotency", + "TaskMessageListResponse", + "TaskMessageMetricsResponse", "TaskMessageRetryPolicy", + "TaskMessageTotals", "TaskRequirements", "TaskRetryPolicy", "TaskSchedule", @@ -185,6 +325,7 @@ "TaskWebhookStatus", "TokenInfo", "Tool", + "ToolA2ASpec", "ToolApproval", "ToolApprovalList", "ToolApprovalSpec", @@ -201,6 +342,7 @@ "ToolRuntimePolicy", "ToolSpec", "ToolStatus", + "ToolWasmSpec", "WatchEvent", "WhoAmI", "Worker", diff --git a/src/orloj_sdk/models/_generated/__init__.py b/src/orloj_sdk/models/_generated/__init__.py new file mode 100644 index 0000000..b3d735d --- /dev/null +++ b/src/orloj_sdk/models/_generated/__init__.py @@ -0,0 +1,2 @@ +"""Generated OpenAPI models (reference + CI freshness gate).""" +from orloj_sdk.models._generated.models import * # noqa: F403 diff --git a/src/orloj_sdk/models/_generated/models.py b/src/orloj_sdk/models/_generated/models.py new file mode 100644 index 0000000..eb1dbf0 --- /dev/null +++ b/src/orloj_sdk/models/_generated/models.py @@ -0,0 +1,1723 @@ +"""AUTO-GENERATED from vendored OpenAPI. Do not edit by hand. + +Regenerate: python scripts/generate_models.py +Source pin: openapi/PIN.json +""" + +# generated by datamodel-code-generator (see scripts/generate_models.py) +# filename: openapi.bundle.yaml + +from __future__ import annotations + +from enum import Enum +from typing import Any + +from pydantic import AnyUrl, AwareDatetime, BaseModel, Field, RootModel + + +class Authentication(BaseModel): + schemes: list[str] | None = None + + +class Provider(BaseModel): + organization: str | None = None + url: AnyUrl | None = None + + +class CardCapabilities(BaseModel): + streaming: bool | None = None + pushNotifications: bool | None = None + stateTransitionHistory: bool | None = None + + +class CardSkill(BaseModel): + id: str + name: str + description: str | None = None + inputSchema: dict[str, Any] | None = None + tags: list[str] | None = None + + +class Jsonrpc(Enum): + field_2_0 = '2.0' + + +class Method(Enum): + tasks_send = 'tasks/send' + tasks_get = 'tasks/get' + tasks_cancel = 'tasks/cancel' + tasks_sendSubscribe = 'tasks/sendSubscribe' + + +class A2AJsonRpcRequest(BaseModel): + jsonrpc: Jsonrpc + id: str | int | None = None + method: Method + params: dict[str, Any] | None = None + + +class A2AJsonRpcError(BaseModel): + code: int + message: str + data: Any | None = None + + +class State(Enum): + submitted = 'submitted' + working = 'working' + input_required = 'input-required' + completed = 'completed' + failed = 'failed' + canceled = 'canceled' + rejected = 'rejected' + + +class A2APart(BaseModel): + type: str + text: str | None = None + data: Any | None = None + metadata: dict[str, Any] | None = None + + +class AgentPolicySpec(BaseModel): + max_tokens_per_run: int | None = None + allowed_models: list[str] | None = None + blocked_tools: list[str] | None = None + apply_mode: str | None = None + target_systems: list[str] | None = None + target_tasks: list[str] | None = None + target_agents: list[str] | None = Field( + None, + description='Agent names this policy applies to. When set, only listed agents are affected; other agents in the same system/task are unconstrained by this policy.', + ) + max_child_depth: int | None = Field( + None, description='Maximum nesting depth for tasks created by orloj.task.create. Default 5.' + ) + max_child_tasks: int | None = Field( + None, + description='Maximum number of child tasks a single task execution can create via orloj.task.create. Default 20.', + ) + + +class PolicyStatus(BaseModel): + phase: str | None = None + lastError: str | None = None + observedGeneration: int | None = None + + +class ApiVersion(Enum): + orloj_dev_v1 = 'orloj.dev/v1' + + +class Kind(Enum): + AgentPolicy = 'AgentPolicy' + + +class AgentRoleSpec(BaseModel): + description: str | None = None + permissions: list[str] | None = None + + +class AgentRoleStatus(PolicyStatus): + pass + + +class Kind1(Enum): + AgentRole = 'AgentRole' + + +class EdgeCondition(BaseModel): + output_contains: str | None = Field( + None, description='Matches if the output contains this string (case-insensitive).' + ) + output_not_contains: str | None = Field( + None, description='Matches if the output does NOT contain this string (case-insensitive).' + ) + output_matches: str | None = Field( + None, description='Matches if the output matches this regex pattern.' + ) + default: bool | None = Field( + None, description='Marks this edge as the fallback when no conditional edge matches.' + ) + output_json_path: str | None = Field( + None, + description='Dot-notation path (e.g. "$.route") to extract a value from JSON output. Requires a comparison operator.', + ) + equals: str | None = Field( + None, description='Matches when the extracted JSON value equals this string.' + ) + not_equals: str | None = Field( + None, description='Matches when the extracted JSON value does NOT equal this string.' + ) + contains: str | None = Field( + None, + description='For arrays, matches when any element equals this value. For strings, matches on substring (case-insensitive).', + ) + greater_than: str | None = Field( + None, description='Matches when the extracted numeric JSON value exceeds this threshold.' + ) + less_than: str | None = Field( + None, description='Matches when the extracted numeric JSON value is below this threshold.' + ) + + +class GraphRoute(BaseModel): + to: str | None = None + labels: dict[str, str] | None = None + policy: dict[str, str] | None = None + condition: EdgeCondition | None = None + + +class GraphJoin(BaseModel): + mode: str | None = None + quorum_count: int | None = None + quorum_percent: int | None = None + on_failure: str | None = None + + +class ReviewCheckpointSpec(BaseModel): + checkpoint_id: str | None = None + display_name: str | None = None + reason: str | None = None + ttl: str | None = None + allow_request_changes: bool = True + max_review_cycles: int = Field(3, ge=1) + + +class Auth(Enum): + public = 'public' + bearer = 'bearer' + + +class AgentSystemA2ASpec(BaseModel): + enabled: bool | None = Field( + None, + description='Expose this AgentSystem through inbound A2A discovery and JSON-RPC invocation.', + ) + auth: Auth = Field( + 'bearer', + description='Controls whether A2A invoke requires authentication for this system. "public" allows unauthenticated invoke; "bearer" (default) requires a valid API token when instance-wide auth is enabled.', + ) + + +class GraphEdge(BaseModel): + next: str | None = None + edges: list[GraphRoute] | None = None + join: GraphJoin | None = None + delegates: list[GraphRoute] | None = Field( + None, + description="Agents dispatched after the node's first execution. Reports flow back and trigger a review re-execution before edges fire.", + ) + delegate_join: GraphJoin | None = Field( + None, description='Controls how delegate returns are collected before the review phase.' + ) + review: ReviewCheckpointSpec | None = Field( + None, + description="Optional human review checkpoint for this node's output before downstream routing continues.", + ) + + +class AgentSystemSpec(BaseModel): + context_adapter: str | None = Field( + None, + description='Optional ContextAdapter resource name. When set, raw task input is passed through the referenced adapter (Tool-backed sanitization) before the first agent in execution order runs.\n', + ) + agents: list[str] | None = None + graph: dict[str, GraphEdge] | None = None + completion_review: ReviewCheckpointSpec | None = Field( + None, + description='Optional final human review checkpoint before the task transitions to Succeeded.', + ) + a2a: AgentSystemA2ASpec | None = None + + +class AgentSystemStatus(PolicyStatus): + pass + + +class Kind2(Enum): + AgentSystem = 'AgentSystem' + + +class AgentLimits(BaseModel): + max_steps: int | None = None + timeout: str | None = None + + +class MemorySpecInline(BaseModel): + ref: str | None = None + type: str | None = None + provider: str | None = None + allow: list[str] | None = None + + +class AgentExecutionSpec(BaseModel): + profile: str | None = None + tool_sequence: list[str] | None = None + required_output_markers: list[str] | None = None + duplicate_tool_call_policy: str | None = None + on_contract_violation: str | None = None + tool_use_behavior: str | None = None + output_schema: dict[str, Any] | None = Field( + None, + description="JSON Schema constraining the agent's output. Passed to model providers for structured output enforcement.", + ) + + +class AgentSpec(BaseModel): + model_ref: str | None = None + fallback_model_refs: list[str] | None = Field( + None, + description='Ordered list of fallback ModelEndpoint refs tried when the primary model_ref fails with a retryable error.', + ) + prompt: str | None = None + tools: list[str] | None = None + allowed_tools: list[str] | None = None + roles: list[str] | None = None + memory: MemorySpecInline | None = None + execution: AgentExecutionSpec | None = None + limits: AgentLimits | None = None + + +class AgentStatus(PolicyStatus): + pass + + +class Kind3(Enum): + Agent = 'Agent' + + +class ObjectMeta(BaseModel): + name: str + namespace: str = 'default' + labels: dict[str, str] | None = None + annotations: dict[str, str] | None = None + resourceVersion: str | None = None + generation: int | None = None + createdAt: str | None = None + + +class ListMeta(BaseModel): + continue_: str | None = Field( + None, + alias='continue', + description='Cursor for next page as scoped namespace/name of the last scanned item; bare names from older clients are still accepted as ?after= values.', + ) + + +class PlainTextError(RootModel[str]): + root: str = Field(..., description='Error body when Content-Type is text/plain.') + + +class Status(Enum): + ok = 'ok' + + +class HealthResponse(BaseModel): + status: Status + + +class NamespacesResponse(BaseModel): + namespaces: list[str] + + +class Capability(BaseModel): + id: str + enabled: bool + description: str | None = None + source: str | None = None + + +class CapabilitySnapshot(BaseModel): + generated_at: str + capabilities: list[Capability] + + +class AuthConfigResponse(BaseModel): + mode: str | None = None + setup_required: bool | None = None + setup_token_required: bool | None = Field( + None, + description='When true, native initial setup requires `setup_token` in `POST /v1/auth/setup` (matches `ORLOJ_SETUP_TOKEN` on the server).', + ) + login_methods: list[str] | None = None + + +class AuthMeResponse(BaseModel): + authenticated: bool | None = None + username: str | None = None + name: str | None = None + role: str | None = None + method: str | None = None + + +class AuthCredentialsRequest(BaseModel): + username: str | None = None + password: str | None = None + setup_token: str | None = None + + +class AuthCLITokenRequest(BaseModel): + username: str + password: str + + +class AuthCLITokenResponse(BaseModel): + name: str | None = Field(None, description='Generated token name') + role: str | None = Field(None, description='Role inherited from the user account') + token: str | None = Field(None, description='Bearer token value (shown only once)') + username: str | None = Field(None, description='Authenticated username') + created_at: AwareDatetime | None = None + + +class AuthChangePasswordRequest(BaseModel): + current_password: str | None = None + new_password: str | None = None + + +class AuthResetPasswordRequest(BaseModel): + username: str + new_password: str + + +class Role(Enum): + admin = 'admin' + writer = 'writer' + reader = 'reader' + controller = 'controller' + a2a = 'a2a' + + +class TokenCreateRequest(BaseModel): + name: str + role: Role + a2a_agent_systems: list[str] | None = Field( + None, + description='Required when role is `a2a`; AgentSystem refs this token may invoke. Bare names resolve in the default namespace.', + ) + + +class TokenMetadata(BaseModel): + name: str | None = None + role: Role | None = None + created_at: str | None = None + a2a_agent_systems: list[str] | None = None + + +class TokenCreateResponse(BaseModel): + name: str | None = None + role: Role | None = None + created_at: str | None = None + token: str | None = None + a2a_agent_systems: list[str] | None = None + + +class TokenListResponse(BaseModel): + items: list[TokenMetadata] | None = None + + +class AuthUserCreateRequest(BaseModel): + username: str + role: str | None = None + + +class AuthUserResponse(BaseModel): + username: str | None = None + role: str | None = None + created_at: str | None = None + updated_at: str | None = None + + +class AuthUserCreateResponse(BaseModel): + username: str | None = None + role: str | None = None + created_at: str | None = None + updated_at: str | None = None + password: str | None = None + + +class AuthUserListResponse(BaseModel): + items: list[AuthUserResponse] | None = None + + +class OkStatusMessage(BaseModel): + status: str | None = None + + +class WebhookDeliveryResponse(BaseModel): + accepted: bool | None = None + duplicate: bool | None = None + event_id: str | None = None + task: str | None = None + message: str | None = None + + +class WatchResourceEvent(BaseModel): + type: str | None = None + resource: dict[str, Any] | None = None + + +class BusEvent(BaseModel): + id: int | None = None + timestamp: str | None = None + source: str | None = None + type: str | None = None + kind: str | None = None + name: str | None = None + namespace: str | None = None + action: str | None = None + message: str | None = None + data: Any | None = None + + +class ToolApprovalDecisionRequest(BaseModel): + decided_by: str | None = None + comment: str | None = None + reason: str | None = None + + +class TaskApprovalDecisionRequest(ToolApprovalDecisionRequest): + pass + + +class TaskApprovalRequestChangesRequest1(BaseModel): + decided_by: str | None = None + comment: str + reason: str | None = None + + +class TaskApprovalRequestChangesRequest2(BaseModel): + decided_by: str | None = None + comment: str | None = None + reason: str + + +class TaskApprovalRequestChangesRequest( + RootModel[TaskApprovalRequestChangesRequest1 | TaskApprovalRequestChangesRequest2] +): + root: TaskApprovalRequestChangesRequest1 | TaskApprovalRequestChangesRequest2 + + +class MemoryEntriesResponse(BaseModel): + entries: list[dict[str, Any]] | None = None + count: int | None = None + + +class NamedLogsResponse(BaseModel): + name: str | None = None + logs: list[str] | None = None + + +class TaskMessageTotals(BaseModel): + messages: int | None = None + queued: int | None = None + running: int | None = None + retrypending: int | None = None + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageAgentMetric(BaseModel): + agent: str | None = None + inbound: int | None = None + outbound: int | None = None + queued: int | None = None + running: int | None = None + retrypending: int | None = None + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageEdgeMetric(BaseModel): + from_agent: str | None = None + to_agent: str | None = None + messages: int | None = None + queued: int | None = None + running: int | None = None + retrypending: int | None = None + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageMetricsResponse(BaseModel): + name: str | None = None + namespace: str | None = None + generated_at: str | None = None + totals: TaskMessageTotals | None = None + per_agent: list[TaskMessageAgentMetric] | None = None + per_edge: list[TaskMessageEdgeMetric] | None = None + filters: dict[str, Any] | None = None + + +class McpServerDeletedResponse(BaseModel): + deleted: str | None = None + + +class StatusEnvelope(BaseModel): + metadata: ObjectMeta | None = None + status: dict[str, Any] | None = None + + +class OnError(Enum): + reject = 'reject' + passthrough = 'passthrough' + + +class ContextAdapterSpec(BaseModel): + tool_ref: str + on_error: OnError | None = None + + +class ContextAdapterStatus(BaseModel): + phase: str | None = None + message: str | None = None + + +class Kind4(Enum): + ContextAdapter = 'ContextAdapter' + + +class ContextAdapter(BaseModel): + apiVersion: ApiVersion + kind: Kind4 + metadata: ObjectMeta + spec: ContextAdapterSpec + status: ContextAdapterStatus | None = None + + +class ContextAdapterList(ListMeta): + items: list[ContextAdapter] + + +class Strategy(Enum): + exact_match = 'exact_match' + llm_judge = 'llm_judge' + manual = 'manual' + custom = 'custom' + + +class EvalScoringConfig(BaseModel): + strategy: Strategy | None = None + model_ref: str | None = None + rubric: str | None = None + tool_ref: str | None = None + + +class EvalExpected(BaseModel): + output_contains: str | None = None + output_not_contains: str | None = None + output_matches: str | None = None + output_json_path: str | None = None + equals: str | None = None + not_equals: str | None = None + contains: str | None = None + greater_than: str | None = None + less_than: str | None = None + + +class EvalSample(BaseModel): + name: str + input: dict[str, str] + expected: EvalExpected | None = None + scoring: EvalScoringConfig | None = None + + +class EvalDatasetSpec(BaseModel): + description: str | None = None + samples: list[EvalSample] + + +class EvalDatasetStatus(BaseModel): + phase: str | None = None + + +class Kind5(Enum): + EvalDataset = 'EvalDataset' + + +class EvalDataset(BaseModel): + apiVersion: ApiVersion + kind: Kind5 + metadata: ObjectMeta + spec: EvalDatasetSpec + status: EvalDatasetStatus | None = None + + +class EvalDatasetList(ListMeta): + items: list[EvalDataset] + + +class AgentOverride(BaseModel): + prompt: str | None = None + model_ref: str | None = None + + +class EvalSampleResult(BaseModel): + sample_name: str + task_name: str | None = None + score: float | None = None + pass_: bool | None = Field(None, alias='pass') + error: str | None = None + latency: str | None = None + tokens: int | None = None + output: str | None = None + reasoning: str | None = None + comment: str | None = None + + +class EvalSummary(BaseModel): + pass_rate: float | None = None + mean_score: float | None = None + p50_latency: str | None = None + p95_latency: str | None = None + total_tokens: int | None = None + total_cost: float | None = None + + +class EvalRunSpec(BaseModel): + dataset_ref: str + system: str + model_endpoint_override: str | None = None + agent_overrides: dict[str, AgentOverride] | None = None + scoring: EvalScoringConfig | None = None + concurrency: int = 5 + timeout: str | None = None + labels: dict[str, str] | None = None + suspended: bool = False + + +class Phase(Enum): + Pending = 'Pending' + Running = 'Running' + Scoring = 'Scoring' + PendingReview = 'PendingReview' + Succeeded = 'Succeeded' + Failed = 'Failed' + Cancelled = 'Cancelled' + + +class EvalRunStatus(BaseModel): + phase: Phase | None = None + totalSamples: int | None = None + completedSamples: int | None = None + passedSamples: int | None = None + failedSamples: int | None = None + erroredSamples: int | None = None + results: list[EvalSampleResult] | None = None + summary: EvalSummary | None = None + datasetGeneration: int | None = None + startedAt: AwareDatetime | None = None + completedAt: AwareDatetime | None = None + cancelledAt: AwareDatetime | None = None + + +class Kind6(Enum): + EvalRun = 'EvalRun' + + +class EvalRun(BaseModel): + apiVersion: ApiVersion + kind: Kind6 + metadata: ObjectMeta + spec: EvalRunSpec + status: EvalRunStatus | None = None + + +class EvalRunList(ListMeta): + items: list[EvalRun] + + +class Summary(BaseModel): + pass_rate: float | None = None + mean_score: float | None = None + total_tokens: int | None = None + + +class Results(BaseModel): + score: float | None = None + pass_: bool | None = Field(None, alias='pass') + + +class Sample(BaseModel): + name: str | None = None + results: dict[str, Results] | None = None + + +class EvalRunCompareResponse(BaseModel): + runs: list[str] | None = None + summary: dict[str, Summary] | None = None + samples: list[Sample] | None = None + + +class McpServerEnvVar(BaseModel): + name: str + value: str | None = None + secretRef: str | None = None + mountPath: str | None = Field( + None, + description='Absolute path inside the container where the resolved value is written as a file. Only valid when spec.image is set.', + ) + + +class McpToolFilter(BaseModel): + include: list[str] | None = None + + +class McpReconnectPolicy(BaseModel): + max_attempts: int | None = None + backoff: str | None = None + + +class Transport(Enum): + stdio = 'stdio' + http = 'http' + + +class McpServerStatus(BaseModel): + phase: str | None = None + discoveredTools: list[str] | None = None + generatedTools: list[str] | None = None + lastSyncedAt: str | None = None + lastError: str | None = None + observedGeneration: int | None = None + + +class Kind7(Enum): + McpServer = 'McpServer' + + +class MemoryAuthConfig(BaseModel): + secretRef: str | None = None + + +class MemoryConfig(BaseModel): + type: str | None = None + provider: str | None = None + embedding_model: str | None = None + endpoint: str | None = None + endpoint_secret_ref: str | None = None + auth: MemoryAuthConfig | None = None + + +class MemoryStatus(PolicyStatus): + pass + + +class Kind8(Enum): + Memory = 'Memory' + + +class Memory(BaseModel): + apiVersion: ApiVersion + kind: Kind8 + metadata: ObjectMeta + spec: MemoryConfig + status: MemoryStatus | None = None + + +class MemoryList(ListMeta): + items: list[Memory] + + +class ModelEndpointAuth(MemoryAuthConfig): + pass + + +class ModelEndpointSpec(BaseModel): + provider: str | None = None + base_url: str | None = None + default_model: str | None = None + options: dict[str, str] | None = None + auth: ModelEndpointAuth | None = None + allowPrivate: bool | None = Field( + None, + description="Permit outbound connections from this endpoint's gateway to\nRFC 1918 / ULA / carrier-grade NAT addresses (e.g. a\nself-hosted Ollama, vLLM, LM Studio, or LiteLLM proxy on a\nprivate network). Loopback, link-local, cloud metadata, and\nunspecified addresses remain blocked regardless. Defaults:\nollama -> true, all other providers -> false.\n", + ) + + +class ModelEndpointStatus(PolicyStatus): + pass + + +class Kind9(Enum): + ModelEndpoint = 'ModelEndpoint' + + +class ModelEndpoint(BaseModel): + apiVersion: ApiVersion + kind: Kind9 + metadata: ObjectMeta + spec: ModelEndpointSpec + status: ModelEndpointStatus | None = None + + +class ModelEndpointList(ListMeta): + items: list[ModelEndpoint] + + +class SealedValue(BaseModel): + keyId: str | None = None + wrappedKey: str | None = Field( + None, description='Base64-encoded RSA-OAEP wrapped AES data key.' + ) + ciphertext: str | None = Field( + None, description='Base64-encoded `nonce || aes_gcm_ciphertext`.' + ) + + +class SealedSecretTemplateSecret(BaseModel): + labels: dict[str, str] | None = None + annotations: dict[str, str] | None = None + + +class SealedSecretSpec(BaseModel): + encryptedData: dict[str, SealedValue] | None = None + template: SealedSecretTemplateSecret | None = None + + +class SealedSecretStatus(PolicyStatus): + pass + + +class Kind10(Enum): + SealedSecret = 'SealedSecret' + + +class SealedSecret(BaseModel): + apiVersion: ApiVersion + kind: Kind10 + metadata: ObjectMeta + spec: SealedSecretSpec + status: SealedSecretStatus | None = None + + +class SealedSecretList(ListMeta): + items: list[SealedSecret] + + +class SealedSecretPublicKeyResponse(BaseModel): + keyId: str + algorithm: str + publicKeyPEM: str = Field( + ..., description='PEM-encoded RSA public key for sealing secrets client-side.' + ) + + +class SecretSpec(BaseModel): + data: dict[str, str] | None = Field( + None, + description='Base64-encoded secret entries. GET responses replace values with `***`; PUT merges `***` for keys that already exist on the server.', + ) + stringData: dict[str, str] | None = Field( + None, + description='Write-only plaintext entries (normalized into `data` as base64). Omitted on GET; on PUT, if present with `***`, values are merged from stored keys like `data`.', + ) + + +class SecretStatus(PolicyStatus): + pass + + +class Kind11(Enum): + Secret = 'Secret' + + +class Secret(BaseModel): + apiVersion: ApiVersion + kind: Kind11 + metadata: ObjectMeta + spec: SecretSpec + status: SecretStatus | None = None + + +class SecretList(ListMeta): + items: list[Secret] + + +class CheckpointType(Enum): + agent_output = 'agent_output' + task_output = 'task_output' + + +class OutputFormat(Enum): + text = 'text' + json = 'json' + + +class TaskApprovalSpec(BaseModel): + task_ref: str + checkpoint_id: str + checkpoint_type: CheckpointType | None = None + agent: str | None = None + reason: str | None = None + ttl: str | None = None + allow_request_changes: bool = True + max_review_cycles: int = Field(3, ge=1) + review_cycle: int = Field(1, ge=1) + supersedes: str | None = None + output: str | None = None + output_format: OutputFormat | None = None + resume_context: dict[str, Any] | None = None + + +class Phase1(Enum): + Pending = 'Pending' + Approved = 'Approved' + Denied = 'Denied' + ChangesRequested = 'ChangesRequested' + Expired = 'Expired' + + +class Decision(Enum): + approved = 'approved' + denied = 'denied' + request_changes = 'request_changes' + + +class TaskApprovalStatus(BaseModel): + phase: Phase1 | None = None + decision: Decision | None = None + decided_by: str | None = None + decided_at: AwareDatetime | None = None + comment: str | None = None + expires_at: AwareDatetime | None = None + + +class Kind12(Enum): + TaskApproval = 'TaskApproval' + + +class TaskApproval(BaseModel): + apiVersion: ApiVersion + kind: Kind12 + metadata: ObjectMeta + spec: TaskApprovalSpec + status: TaskApprovalStatus | None = None + + +class TaskApprovalList(ListMeta): + items: list[TaskApproval] + + +class TaskScheduleStatus(BaseModel): + phase: str | None = None + lastError: str | None = None + lastScheduleTime: str | None = None + lastSuccessfulTime: str | None = None + nextScheduleTime: str | None = None + lastTriggeredTask: str | None = None + activeRuns: list[str] | None = None + observedGeneration: int | None = None + + +class Kind13(Enum): + TaskSchedule = 'TaskSchedule' + + +class TaskWebhookAuthSpec(BaseModel): + profile: str | None = Field( + None, description='Auth profile: generic, github, hmac, or shared_token' + ) + secret_ref: str | None = None + signature_header: str | None = None + signature_prefix: str | None = None + timestamp_header: str | None = None + max_skew_seconds: int | None = None + algorithm: str | None = Field( + None, + description='HMAC algorithm: sha256 (default), sha1, or sha512. Used with hmac profile.', + ) + payload_format: str | None = Field( + None, + description='HMAC payload construction: body, timestamp_dot_body, or prefix_timestamp_body. Used with hmac profile.', + ) + payload_prefix: str | None = Field( + None, description='Literal prefix for prefix_timestamp_body format (e.g. v0 for Slack).' + ) + payload_separator: str | None = Field( + None, + description='Separator between payload parts (default dot). Used with prefix_timestamp_body.', + ) + signature_encoding: str | None = Field( + None, description='Signature encoding: hex (default) or base64. Used with hmac profile.' + ) + header_format: str | None = Field( + None, + description='How to parse the signature header: plain (default) or kv_pairs (comma-separated key=value).', + ) + signature_key: str | None = Field( + None, description='Key holding the signature in a kv_pairs header (e.g. v1 for Stripe).' + ) + timestamp_key: str | None = Field( + None, description='Key holding the timestamp in a kv_pairs header (e.g. t for Stripe).' + ) + + +class TaskWebhookIdempotency(BaseModel): + event_id_header: str | None = Field( + None, + description='HTTP header name containing the event ID for deduplication (e.g. X-Event-Id, X-GitHub-Delivery).', + ) + event_id_from_body: str | None = Field( + None, + description='Dot-separated field path to extract the event ID from the JSON request body (e.g. update_id, data.id). Used when the sender puts the dedup key in the body instead of a header.', + ) + dedupe_window_seconds: int | None = None + + +class TaskWebhookPayloadSpec(BaseModel): + mode: str | None = None + input_key: str | None = None + + +class TaskWebhookStatus(BaseModel): + phase: str | None = None + lastError: str | None = None + observedGeneration: int | None = None + endpointID: str | None = None + endpointPath: str | None = None + lastDeliveryTime: str | None = None + lastEventID: str | None = None + lastTriggeredTask: str | None = None + acceptedCount: int | None = None + duplicateCount: int | None = None + rejectedCount: int | None = None + + +class Kind14(Enum): + TaskWebhook = 'TaskWebhook' + + +class TaskRetryPolicy(McpReconnectPolicy): + pass + + +class TaskMessageRetryPolicy(BaseModel): + max_attempts: int | None = None + backoff: str | None = None + max_backoff: str | None = None + jitter: str | None = None + non_retryable: list[str] | None = None + + +class TaskRequirements(BaseModel): + region: str | None = None + gpu: bool | None = None + model: str | None = None + + +class TaskSpec(BaseModel): + system: str | None = None + mode: str | None = None + input: dict[str, str] | None = None + priority: str | None = None + max_turns: int | None = None + retry: TaskRetryPolicy | None = None + message_retry: TaskMessageRetryPolicy | None = None + requirements: TaskRequirements | None = None + + +class TaskTraceEvent(BaseModel): + timestamp: str | None = None + step_id: str | None = None + attempt: int | None = None + step: int | None = None + branch_id: str | None = None + type: str | None = None + agent: str | None = None + tool: str | None = None + tool_contract_version: str | None = None + tool_request_id: str | None = None + tool_attempt: int | None = None + error_code: str | None = None + error_reason: str | None = None + retryable: bool | None = None + message: str | None = None + latency_ms: int | None = None + tokens: int | None = None + input_tokens: int | None = None + output_tokens: int | None = None + token_usage_source: str | None = None + tool_calls: int | None = None + memory_writes: int | None = None + tool_auth_profile: str | None = None + tool_auth_secret_ref: str | None = None + + +class TaskHistoryEvent(BaseModel): + timestamp: str | None = None + type: str | None = None + worker: str | None = None + message: str | None = None + + +class TaskMessage(BaseModel): + timestamp: str | None = None + message_id: str | None = None + idempotency_key: str | None = None + task_id: str | None = None + attempt: int | None = None + system: str | None = None + from_agent: str | None = None + to_agent: str | None = None + branch_id: str | None = None + parent_branch_id: str | None = None + type: str | None = None + content: str | None = None + trace_id: str | None = None + parent_id: str | None = None + phase: str | None = None + attempts: int | None = None + max_attempts: int | None = None + last_error: str | None = None + worker: str | None = None + processed_at: str | None = None + next_attempt_at: str | None = None + + +class TaskMessageIdempotency(BaseModel): + key: str | None = None + message_id: str | None = None + state: str | None = None + updated_at: str | None = None + expires_at: str | None = None + worker: str | None = None + + +class TaskJoinSource(BaseModel): + message_id: str | None = None + from_agent: str | None = None + branch_id: str | None = None + timestamp: str | None = None + payload: str | None = None + + +class TaskJoinState(BaseModel): + attempt: int | None = None + node: str | None = None + mode: str | None = None + expected: int | None = None + quorum_required: int | None = None + activated: bool | None = None + activated_at: str | None = None + activated_by: str | None = None + sources: list[TaskJoinSource] | None = None + + +class TaskDelegationState(TaskJoinState): + pass + + +class TaskBlockedOn(BaseModel): + kind: str | None = None + name: str | None = None + reason: str | None = None + + +class TaskStatus(BaseModel): + phase: str | None = None + lastError: str | None = None + startedAt: str | None = None + completedAt: str | None = None + nextAttemptAt: str | None = None + attempts: int | None = None + output: dict[str, str] | None = None + assignedWorker: str | None = None + claimedBy: str | None = None + leaseUntil: str | None = None + lastHeartbeat: str | None = None + trace: list[TaskTraceEvent] | None = None + history: list[TaskHistoryEvent] | None = None + messages: list[TaskMessage] | None = None + message_idempotency: list[TaskMessageIdempotency] | None = None + join_states: list[TaskJoinState] | None = None + delegation_states: list[TaskDelegationState] | None = None + blocked_on: TaskBlockedOn | None = None + observedGeneration: int | None = None + + +class Kind15(Enum): + Task = 'Task' + + +class Task(BaseModel): + apiVersion: ApiVersion + kind: Kind15 + metadata: ObjectMeta + spec: TaskSpec + status: TaskStatus | None = None + + +class TaskList(ListMeta): + items: list[Task] + + +class ToolApprovalSpec(BaseModel): + task_ref: str + tool: str + operation_class: str | None = None + agent: str | None = None + input: str | None = None + reason: str | None = None + ttl: str | None = None + + +class ToolApprovalStatus(BaseModel): + phase: str | None = None + decision: str | None = None + decided_by: str | None = None + decided_at: str | None = None + comment: str | None = None + expires_at: str | None = None + + +class Kind16(Enum): + ToolApproval = 'ToolApproval' + + +class ToolApproval(BaseModel): + apiVersion: ApiVersion + kind: Kind16 + metadata: ObjectMeta + spec: ToolApprovalSpec + status: ToolApprovalStatus | None = None + + +class ToolApprovalList(ListMeta): + items: list[ToolApproval] + + +class OperationRule(BaseModel): + operation_class: str | None = None + verdict: str | None = None + + +class ToolPermissionSpec(BaseModel): + tool_ref: str | None = None + action: str | None = None + required_permissions: list[str] | None = None + match_mode: str | None = None + apply_mode: str | None = None + target_agents: list[str] | None = None + operation_rules: list[OperationRule] | None = None + + +class ToolPermissionStatus(PolicyStatus): + pass + + +class Kind17(Enum): + ToolPermission = 'ToolPermission' + + +class ToolPermission(BaseModel): + apiVersion: ApiVersion + kind: Kind17 + metadata: ObjectMeta + spec: ToolPermissionSpec + status: ToolPermissionStatus | None = None + + +class ToolPermissionList(ListMeta): + items: list[ToolPermission] + + +class ToolRetryPolicy(BaseModel): + max_attempts: int | None = None + backoff: str | None = None + max_backoff: str | None = None + jitter: str | None = None + + +class ToolRuntimePolicy(BaseModel): + timeout: str | None = None + isolation_mode: str | None = None + retry: ToolRetryPolicy | None = None + + +class ToolAuth(BaseModel): + profile: str | None = None + secretRef: str | None = None + headerName: str | None = None + tokenURL: str | None = None + scopes: list[str] | None = None + + +class ToolCliEnvRef(BaseModel): + name: str + secretRef: str + key: str | None = None + + +class ContainerResources(BaseModel): + memory: str | None = Field( + None, + description='Container memory limit (e.g. 128m, 1g). Overrides --tool-container-memory.', + ) + cpus: str | None = Field( + None, description='Container CPU limit (e.g. 0.50, 1.0). Overrides --tool-container-cpus.' + ) + pids_limit: int | None = Field( + None, description='Container PIDs limit. Overrides --tool-container-pids-limit.' + ) + + +class ToolCliSpec(BaseModel): + command: str | None = None + args: list[str] | None = None + image: str | None = None + image_pull_secret: str | None = Field( + None, description='Name of a Secret containing registry credentials for pulling cli.image.' + ) + network: str | None = None + stdin_from_input: bool | None = None + output: str | None = None + working_dir: str | None = None + env: dict[str, str] | None = None + env_from: list[ToolCliEnvRef] | None = None + resources: ContainerResources | None = None + + +class ToolWasmSpec(BaseModel): + module: str | None = Field( + None, description='Local path, HTTPS URL, or OCI reference (oci://...) to the .wasm module' + ) + entrypoint: str | None = Field( + None, description='Exported function name to call (default: run)' + ) + max_memory_bytes: int | None = Field( + None, description='Maximum memory in bytes (default: 64MB)' + ) + fuel: int | None = Field(None, description='Maximum fuel units for execution metering') + enable_wasi: bool | None = Field( + None, description='Enable WASI capabilities (stdin/stdout/stderr)' + ) + image_pull_secret: str | None = Field( + None, + description='Name of a Secret containing registry credentials for pulling OCI-referenced WASM modules.', + ) + + +class Type(Enum): + http = 'http' + external = 'external' + grpc = 'grpc' + webhook_callback = 'webhook-callback' + mcp = 'mcp' + wasm = 'wasm' + cli = 'cli' + a2a = 'a2a' + + +class ToolA2ASpec(BaseModel): + agent_url: AnyUrl | None = Field(None, description='Remote A2A agent endpoint URL or base URL') + protocol_version: str | None = Field( + None, description='A2A protocol version (defaults to server-configured version)' + ) + prefer_streaming: bool | None = Field( + None, description='Prefer streaming (sendSubscribe) when the remote agent supports it' + ) + + +class ToolStatus(PolicyStatus): + pass + + +class Kind18(Enum): + Tool = 'Tool' + + +class WorkerCapabilities(BaseModel): + gpu: bool | None = None + supported_models: list[str] | None = None + + +class WorkerSpec(BaseModel): + region: str | None = None + capabilities: WorkerCapabilities | None = None + max_concurrent_tasks: int | None = None + + +class WorkerStatus(BaseModel): + phase: str | None = None + lastError: str | None = None + lastHeartbeat: str | None = None + observedGeneration: int | None = None + currentTasks: int | None = None + + +class Kind19(Enum): + Worker = 'Worker' + + +class Worker(BaseModel): + apiVersion: ApiVersion + kind: Kind19 + metadata: ObjectMeta + spec: WorkerSpec + status: WorkerStatus | None = None + + +class WorkerList(ListMeta): + items: list[Worker] + + +class AgentCard(BaseModel): + name: str = Field(..., description='Human-readable agent name') + description: str | None = Field(None, description='Agent description') + url: AnyUrl = Field(..., description='A2A JSON-RPC endpoint URL') + version: str | None = None + protocolVersion: str | None = Field(None, description='A2A protocol version') + capabilities: CardCapabilities | None = None + skills: list[CardSkill] | None = None + authentication: Authentication | None = None + provider: Provider | None = None + + +class A2AJsonRpcResponse(BaseModel): + jsonrpc: Jsonrpc + id: str | int | None = None + result: dict[str, Any] | None = None + error: A2AJsonRpcError | None = None + + +class A2AArtifact(BaseModel): + name: str | None = None + description: str | None = None + parts: list[A2APart] | None = None + index: int | None = None + + +class A2AMessage(BaseModel): + role: str + parts: list[A2APart] + + +class RemoteAgentEntry(BaseModel): + name: str + url: AnyUrl + protocolVersion: str | None = None + cacheStatus: str | None = None + lastRefreshed: AwareDatetime | None = None + cacheTTL: str | None = None + error: str | None = None + card: AgentCard | None = None + + +class A2ARegistryResponse(BaseModel): + localAgents: list[AgentCard] | None = None + remoteAgents: list[RemoteAgentEntry] | None = None + + +class AgentPolicy(BaseModel): + apiVersion: ApiVersion + kind: Kind + metadata: ObjectMeta + spec: AgentPolicySpec + status: PolicyStatus | None = None + + +class AgentPolicyList(ListMeta): + items: list[AgentPolicy] + + +class AgentRole(BaseModel): + apiVersion: ApiVersion + kind: Kind1 + metadata: ObjectMeta + spec: AgentRoleSpec + status: AgentRoleStatus | None = None + + +class AgentRoleList(ListMeta): + items: list[AgentRole] + + +class AgentSystem(BaseModel): + apiVersion: ApiVersion + kind: Kind2 + metadata: ObjectMeta + spec: AgentSystemSpec + status: AgentSystemStatus | None = None + + +class AgentSystemList(ListMeta): + items: list[AgentSystem] + + +class Agent(BaseModel): + apiVersion: ApiVersion + kind: Kind3 + metadata: ObjectMeta + spec: AgentSpec + status: AgentStatus | None = None + + +class AgentList(ListMeta): + items: list[Agent] + + +class TaskMessageListResponse(BaseModel): + name: str | None = None + namespace: str | None = None + total: int | None = None + filtered_from: int | None = None + lifecycle_counts: dict[str, int] | None = None + messages: list[TaskMessage] | None = None + + +class McpServerSpec(BaseModel): + transport: Transport + command: str | None = None + args: list[str] | None = None + env: list[McpServerEnvVar] | None = None + endpoint: str | None = None + allowPrivate: bool | None = Field( + None, + description="Permit this MCP server's HTTP transport to connect to RFC 1918 /\nULA / carrier-grade NAT addresses, such as trusted in-cluster\nservices. Loopback, link-local, cloud metadata, and unspecified\naddresses remain blocked regardless. Defaults to false and has no\neffect on stdio transport.\n", + ) + image: str | None = Field( + None, + description='Container image for stdio transport. When set, the MCP server runs inside a Docker container.', + ) + image_pull_secret: str | None = Field( + None, description='Name of a Secret containing registry credentials for pulling spec.image.' + ) + idle_timeout: str = Field( + '0', + description='Duration after which an idle session is reaped (e.g. 5m). 0 means never evict.', + ) + auth: ToolAuth | None = None + tool_filter: McpToolFilter | None = None + reconnect: McpReconnectPolicy | None = None + resources: ContainerResources | None = None + default_tool_runtime: ToolRuntimePolicy | None = None + + +class McpServer(BaseModel): + apiVersion: ApiVersion + kind: Kind7 + metadata: ObjectMeta + spec: McpServerSpec + status: McpServerStatus | None = None + + +class McpServerList(ListMeta): + items: list[McpServer] + + +class TaskScheduleSpec(BaseModel): + task_ref: str | None = Field( + None, + description='Reference to a template Task (name or namespace/name). Mutually exclusive with task_template.', + ) + task_template: TaskSpec | None = Field( + None, + description='Inline task spec used instead of a separate template Task. Mutually exclusive with task_ref.', + ) + schedule: str | None = None + time_zone: str | None = None + suspend: bool | None = None + starting_deadline_seconds: int | None = None + concurrency_policy: str | None = None + successful_history_limit: int | None = None + failed_history_limit: int | None = None + + +class TaskSchedule(BaseModel): + apiVersion: ApiVersion + kind: Kind13 + metadata: ObjectMeta + spec: TaskScheduleSpec + status: TaskScheduleStatus | None = None + + +class TaskScheduleList(ListMeta): + items: list[TaskSchedule] + + +class TaskWebhookSpec(BaseModel): + task_ref: str | None = Field( + None, + description='Reference to a template Task (name or namespace/name). Mutually exclusive with task_template.', + ) + task_template: TaskSpec | None = Field( + None, + description='Inline task spec used instead of a separate template Task. Mutually exclusive with task_ref.', + ) + suspend: bool | None = None + auth: TaskWebhookAuthSpec | None = None + idempotency: TaskWebhookIdempotency | None = None + payload: TaskWebhookPayloadSpec | None = None + + +class TaskWebhook(BaseModel): + apiVersion: ApiVersion + kind: Kind14 + metadata: ObjectMeta + spec: TaskWebhookSpec + status: TaskWebhookStatus | None = None + + +class TaskWebhookList(BaseModel): + items: list[TaskWebhook] | None = None + + +class ToolSpec(BaseModel): + type: Type | None = None + endpoint: str | None = None + description: str | None = None + input_schema: dict[str, Any] | None = None + mcp_server_ref: str | None = None + mcp_tool_name: str | None = None + cli: ToolCliSpec | None = None + wasm: ToolWasmSpec | None = None + capabilities: list[str] | None = None + operation_classes: list[str] | None = None + risk_level: str | None = None + runtime: ToolRuntimePolicy | None = None + auth: ToolAuth | None = None + a2a: ToolA2ASpec | None = None + + +class Tool(BaseModel): + apiVersion: ApiVersion + kind: Kind18 + metadata: ObjectMeta + spec: ToolSpec + status: ToolStatus | None = None + + +class ToolList(ListMeta): + items: list[Tool] + + +class A2AStatus(BaseModel): + state: State + message: A2AMessage | None = None + + +class A2ATask(BaseModel): + id: str + status: A2AStatus + artifacts: list[A2AArtifact] | None = None + history: list[A2AMessage] | None = None + metadata: dict[str, str] | None = None diff --git a/src/orloj_sdk/models/a2a.py b/src/orloj_sdk/models/a2a.py new file mode 100644 index 0000000..fe19ed3 --- /dev/null +++ b/src/orloj_sdk/models/a2a.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import Field + +from orloj_sdk.models._base import OrlojModel + + +class CardCapabilities(OrlojModel): + streaming: bool | None = None + push_notifications: bool | None = Field(None, alias="pushNotifications") + state_transition_history: bool | None = Field(None, alias="stateTransitionHistory") + + +class CardSkill(OrlojModel): + id: str + name: str + description: str | None = None + input_schema: dict[str, Any] | None = Field(None, alias="inputSchema") + tags: list[str] | None = None + + +class AgentCardAuthentication(OrlojModel): + schemes: list[str] | None = None + + +class AgentCardProvider(OrlojModel): + organization: str | None = None + url: str | None = None + + +class AgentCard(OrlojModel): + name: str + url: str + description: str | None = None + version: str | None = None + protocol_version: str | None = Field(None, alias="protocolVersion") + capabilities: CardCapabilities | None = None + skills: list[CardSkill] | None = None + authentication: AgentCardAuthentication | None = None + provider: AgentCardProvider | None = None + + +class A2AJsonRpcError(OrlojModel): + code: int + message: str + data: Any | None = None + + +class A2AJsonRpcRequest(OrlojModel): + jsonrpc: str = "2.0" + method: str + id: str | int | None = None + params: dict[str, Any] | None = None + + +class A2AJsonRpcResponse(OrlojModel): + jsonrpc: str | None = None + id: str | int | None = None + result: dict[str, Any] | None = None + error: A2AJsonRpcError | None = None + + +class A2APart(OrlojModel): + type: str + text: str | None = None + data: Any | None = None + metadata: dict[str, Any] | None = None + + +class A2AMessage(OrlojModel): + role: str + parts: list[A2APart] + + +class A2AArtifact(OrlojModel): + name: str | None = None + description: str | None = None + parts: list[A2APart] | None = None + index: int | None = None + + +class A2AStatus(OrlojModel): + state: str + message: A2AMessage | None = None + + +class A2ATask(OrlojModel): + id: str + status: A2AStatus + artifacts: list[A2AArtifact] | None = None + history: list[A2AMessage] | None = None + metadata: dict[str, str] | None = None + + +class RemoteAgentEntry(OrlojModel): + name: str + url: str + protocol_version: str | None = Field(None, alias="protocolVersion") + cache_status: str | None = Field(None, alias="cacheStatus") + last_refreshed: str | None = Field(None, alias="lastRefreshed") + cache_ttl: str | None = Field(None, alias="cacheTTL") + error: str | None = None + card: AgentCard | None = None + + +class A2ARegistryResponse(OrlojModel): + local_agents: list[AgentCard] | None = Field(None, alias="localAgents") + remote_agents: list[RemoteAgentEntry] | None = Field(None, alias="remoteAgents") diff --git a/src/orloj_sdk/models/agent.py b/src/orloj_sdk/models/agent.py index a884ee3..3e60c8b 100644 --- a/src/orloj_sdk/models/agent.py +++ b/src/orloj_sdk/models/agent.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import Any + from pydantic import Field from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel @@ -26,10 +28,12 @@ class AgentExecutionSpec(OrlojModel): duplicate_tool_call_policy: str | None = None on_contract_violation: str | None = None tool_use_behavior: str | None = None + output_schema: dict[str, Any] | None = None class AgentSpec(OrlojModel): model_ref: str | None = None + fallback_model_refs: list[str] | None = None prompt: str | None = None tools: list[str] | None = None allowed_tools: list[str] | None = None diff --git a/src/orloj_sdk/models/agent_policy.py b/src/orloj_sdk/models/agent_policy.py index 32d065d..a9c82f1 100644 --- a/src/orloj_sdk/models/agent_policy.py +++ b/src/orloj_sdk/models/agent_policy.py @@ -12,6 +12,9 @@ class AgentPolicySpec(OrlojModel): apply_mode: str | None = None target_systems: list[str] | None = None target_tasks: list[str] | None = None + target_agents: list[str] | None = None + max_child_depth: int | None = None + max_child_tasks: int | None = None class PolicyStatus(OrlojModel): diff --git a/src/orloj_sdk/models/agent_system.py b/src/orloj_sdk/models/agent_system.py index bf1a76c..dc5b114 100644 --- a/src/orloj_sdk/models/agent_system.py +++ b/src/orloj_sdk/models/agent_system.py @@ -5,10 +5,24 @@ from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel +class EdgeCondition(OrlojModel): + output_contains: str | None = None + output_not_contains: str | None = None + output_matches: str | None = None + default: bool | None = None + output_json_path: str | None = None + equals: str | None = None + not_equals: str | None = None + contains: str | None = None + greater_than: str | None = None + less_than: str | None = None + + class GraphRoute(OrlojModel): to: str | None = None labels: dict[str, str] | None = None policy: dict[str, str] | None = None + condition: EdgeCondition | None = None class GraphJoin(OrlojModel): @@ -18,15 +32,35 @@ class GraphJoin(OrlojModel): on_failure: str | None = None +class ReviewCheckpointSpec(OrlojModel): + checkpoint_id: str | None = None + display_name: str | None = None + reason: str | None = None + ttl: str | None = None + allow_request_changes: bool | None = None + max_review_cycles: int | None = None + + +class AgentSystemA2ASpec(OrlojModel): + enabled: bool | None = None + auth: str | None = None + + class GraphEdge(OrlojModel): next: str | None = None edges: list[GraphRoute] | None = None join: GraphJoin | None = None + delegates: list[GraphRoute] | None = None + delegate_join: GraphJoin | None = None + review: ReviewCheckpointSpec | None = None class AgentSystemSpec(OrlojModel): + context_adapter: str | None = None agents: list[str] | None = None graph: dict[str, GraphEdge] | None = None + completion_review: ReviewCheckpointSpec | None = None + a2a: AgentSystemA2ASpec | None = None class AgentSystemStatus(OrlojModel): diff --git a/src/orloj_sdk/models/context_adapter.py b/src/orloj_sdk/models/context_adapter.py new file mode 100644 index 0000000..582e002 --- /dev/null +++ b/src/orloj_sdk/models/context_adapter.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from pydantic import Field + +from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel + + +class ContextAdapterSpec(OrlojModel): + tool_ref: str + on_error: str | None = None + + +class ContextAdapterStatus(OrlojModel): + phase: str | None = None + message: str | None = None + + +class ContextAdapter(OrlojModel): + api_version: str = Field("orloj.dev/v1", alias="apiVersion") + kind: str = "ContextAdapter" + metadata: ObjectMeta + spec: ContextAdapterSpec + status: ContextAdapterStatus | None = None + + +class ContextAdapterList(OrlojModel): + metadata: ListMeta | None = None + items: list[ContextAdapter] diff --git a/src/orloj_sdk/models/eval.py b/src/orloj_sdk/models/eval.py new file mode 100644 index 0000000..e6b3f20 --- /dev/null +++ b/src/orloj_sdk/models/eval.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import Field + +from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel + + +class EvalScoringConfig(OrlojModel): + strategy: str | None = None + model_ref: str | None = None + rubric: str | None = None + tool_ref: str | None = None + + +class EvalExpected(OrlojModel): + output_contains: str | None = None + output_not_contains: str | None = None + output_matches: str | None = None + output_json_path: str | None = None + equals: str | None = None + not_equals: str | None = None + contains: str | None = None + greater_than: str | None = None + less_than: str | None = None + + +class EvalSample(OrlojModel): + name: str + input: dict[str, str] + expected: EvalExpected | None = None + scoring: EvalScoringConfig | None = None + + +class EvalDatasetSpec(OrlojModel): + description: str | None = None + samples: list[EvalSample] + + +class EvalDatasetStatus(OrlojModel): + phase: str | None = None + + +class EvalDataset(OrlojModel): + api_version: str = Field("orloj.dev/v1", alias="apiVersion") + kind: str = "EvalDataset" + metadata: ObjectMeta + spec: EvalDatasetSpec + status: EvalDatasetStatus | None = None + + +class EvalDatasetList(OrlojModel): + metadata: ListMeta | None = None + items: list[EvalDataset] + + +class AgentOverride(OrlojModel): + prompt: str | None = None + model_ref: str | None = None + + +class EvalSampleResult(OrlojModel): + sample_name: str + task_name: str | None = None + score: float | None = None + # `pass` is a Python keyword; wire field is "pass" + passed: bool | None = Field(None, alias="pass") + error: str | None = None + latency: str | None = None + tokens: int | None = None + output: str | None = None + reasoning: str | None = None + comment: str | None = None + + +class EvalSummary(OrlojModel): + pass_rate: float | None = None + mean_score: float | None = None + p50_latency: str | None = None + p95_latency: str | None = None + total_tokens: int | None = None + total_cost: float | None = None + + +class EvalRunSpec(OrlojModel): + dataset_ref: str + system: str + model_endpoint_override: str | None = None + agent_overrides: dict[str, AgentOverride] | None = None + scoring: EvalScoringConfig | None = None + concurrency: int | None = None + timeout: str | None = None + labels: dict[str, str] | None = None + suspended: bool | None = None + + +class EvalRunStatus(OrlojModel): + phase: str | None = None + total_samples: int | None = Field(None, alias="totalSamples") + completed_samples: int | None = Field(None, alias="completedSamples") + passed_samples: int | None = Field(None, alias="passedSamples") + failed_samples: int | None = Field(None, alias="failedSamples") + errored_samples: int | None = Field(None, alias="erroredSamples") + results: list[EvalSampleResult] | None = None + summary: EvalSummary | None = None + dataset_generation: int | None = Field(None, alias="datasetGeneration") + started_at: str | None = Field(None, alias="startedAt") + completed_at: str | None = Field(None, alias="completedAt") + cancelled_at: str | None = Field(None, alias="cancelledAt") + + +class EvalRun(OrlojModel): + api_version: str = Field("orloj.dev/v1", alias="apiVersion") + kind: str = "EvalRun" + metadata: ObjectMeta + spec: EvalRunSpec + status: EvalRunStatus | None = None + + +class EvalRunList(OrlojModel): + metadata: ListMeta | None = None + items: list[EvalRun] + + +class EvalRunCompareSampleResult(OrlojModel): + score: float | None = None + passed: bool | None = Field(None, alias="pass") + + +class EvalRunCompareSample(OrlojModel): + name: str | None = None + results: dict[str, EvalRunCompareSampleResult] | None = None + + +class EvalRunCompareSummary(OrlojModel): + pass_rate: float | None = None + mean_score: float | None = None + total_tokens: int | None = None + + +class EvalRunCompareResponse(OrlojModel): + runs: list[str] | None = None + summary: dict[str, EvalRunCompareSummary] | None = None + samples: list[EvalRunCompareSample] | None = None + + +class EvalSampleAnnotation(OrlojModel): + score: float | None = None + passed: bool | None = Field(None, alias="pass") + comment: str | None = None + + +class EvalBulkImportBody(OrlojModel): + """Body for PUT /v1/eval-runs/{name}/results (bulk annotation import).""" + + annotations: list[dict[str, Any]] | None = None diff --git a/src/orloj_sdk/models/mcp_server.py b/src/orloj_sdk/models/mcp_server.py index b09c08b..6824181 100644 --- a/src/orloj_sdk/models/mcp_server.py +++ b/src/orloj_sdk/models/mcp_server.py @@ -3,13 +3,14 @@ from pydantic import Field from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel -from orloj_sdk.models.tool import ToolAuth +from orloj_sdk.models.tool import ContainerResources, ToolAuth, ToolRuntimePolicy class McpServerEnvVar(OrlojModel): name: str value: str | None = None secret_ref: str | None = Field(None, alias="secretRef") + mount_path: str | None = Field(None, alias="mountPath") class McpToolFilter(OrlojModel): @@ -27,9 +28,15 @@ class McpServerSpec(OrlojModel): args: list[str] | None = None env: list[McpServerEnvVar] | None = None endpoint: str | None = None + allow_private: bool | None = Field(None, alias="allowPrivate") + image: str | None = None + image_pull_secret: str | None = None + idle_timeout: str | None = None auth: ToolAuth | None = None tool_filter: McpToolFilter | None = None reconnect: McpReconnectPolicy | None = None + resources: ContainerResources | None = None + default_tool_runtime: ToolRuntimePolicy | None = None class McpServerStatus(OrlojModel): diff --git a/src/orloj_sdk/models/memory.py b/src/orloj_sdk/models/memory.py index d9121ce..f256c80 100644 --- a/src/orloj_sdk/models/memory.py +++ b/src/orloj_sdk/models/memory.py @@ -16,6 +16,7 @@ class MemoryConfig(OrlojModel): provider: str | None = None embedding_model: str | None = None endpoint: str | None = None + endpoint_secret_ref: str | None = None auth: MemoryAuthConfig | None = None diff --git a/src/orloj_sdk/models/model_endpoint.py b/src/orloj_sdk/models/model_endpoint.py index 67c8c27..ec9cd44 100644 --- a/src/orloj_sdk/models/model_endpoint.py +++ b/src/orloj_sdk/models/model_endpoint.py @@ -15,6 +15,7 @@ class ModelEndpointSpec(OrlojModel): default_model: str | None = None options: dict[str, str] | None = None auth: ModelEndpointAuth | None = None + allow_private: bool | None = Field(None, alias="allowPrivate") class ModelEndpointStatus(OrlojModel): diff --git a/src/orloj_sdk/models/observability.py b/src/orloj_sdk/models/observability.py new file mode 100644 index 0000000..a785e13 --- /dev/null +++ b/src/orloj_sdk/models/observability.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import Field + +from orloj_sdk.models._base import OrlojModel +from orloj_sdk.models.task import TaskMessage + + +class TaskMessageListResponse(OrlojModel): + name: str | None = None + namespace: str | None = None + total: int | None = None + filtered_from: int | None = None + lifecycle_counts: dict[str, int] | None = None + messages: list[TaskMessage] | None = None + + +class TaskMessageTotals(OrlojModel): + messages: int | None = None + queued: int | None = None + running: int | None = None + retry_pending: int | None = Field(None, alias="retrypending") + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageAgentMetric(OrlojModel): + agent: str | None = None + inbound: int | None = None + outbound: int | None = None + queued: int | None = None + running: int | None = None + retry_pending: int | None = Field(None, alias="retrypending") + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageEdgeMetric(OrlojModel): + from_agent: str | None = None + to_agent: str | None = None + messages: int | None = None + queued: int | None = None + running: int | None = None + retry_pending: int | None = Field(None, alias="retrypending") + succeeded: int | None = None + deadletter: int | None = None + in_flight: int | None = None + retry_count: int | None = None + deadletters: int | None = None + latency_ms_avg: int | None = None + latency_ms_p95: int | None = None + latency_sample_size: int | None = None + + +class TaskMessageMetricsResponse(OrlojModel): + name: str | None = None + namespace: str | None = None + generated_at: str | None = None + totals: TaskMessageTotals | None = None + per_agent: list[TaskMessageAgentMetric] | None = None + per_edge: list[TaskMessageEdgeMetric] | None = None + filters: dict[str, Any] | None = None + + +class CLITokenResponse(OrlojModel): + name: str + role: str | None = None + token: str | None = None + username: str | None = None + created_at: str | None = None diff --git a/src/orloj_sdk/models/sealed_secret.py b/src/orloj_sdk/models/sealed_secret.py new file mode 100644 index 0000000..a7253c9 --- /dev/null +++ b/src/orloj_sdk/models/sealed_secret.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from pydantic import Field + +from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel + + +class SealedValue(OrlojModel): + key_id: str | None = Field(None, alias="keyId") + wrapped_key: str | None = Field(None, alias="wrappedKey") + ciphertext: str | None = None + + +class SealedSecretTemplateSecret(OrlojModel): + labels: dict[str, str] | None = None + annotations: dict[str, str] | None = None + + +class SealedSecretSpec(OrlojModel): + encrypted_data: dict[str, SealedValue] | None = Field(None, alias="encryptedData") + template: SealedSecretTemplateSecret | None = None + + +class SealedSecretStatus(OrlojModel): + phase: str | None = None + last_error: str | None = Field(None, alias="lastError") + observed_generation: int | None = Field(None, alias="observedGeneration") + + +class SealedSecret(OrlojModel): + api_version: str = Field("orloj.dev/v1", alias="apiVersion") + kind: str = "SealedSecret" + metadata: ObjectMeta + spec: SealedSecretSpec + status: SealedSecretStatus | None = None + + +class SealedSecretList(OrlojModel): + metadata: ListMeta | None = None + items: list[SealedSecret] + + +class SealedSecretPublicKeyResponse(OrlojModel): + key_id: str = Field(..., alias="keyId") + algorithm: str + public_key_pem: str = Field(..., alias="publicKeyPEM") diff --git a/src/orloj_sdk/models/task.py b/src/orloj_sdk/models/task.py index 5123bf2..9f53234 100644 --- a/src/orloj_sdk/models/task.py +++ b/src/orloj_sdk/models/task.py @@ -113,6 +113,33 @@ class TaskJoinState(OrlojModel): sources: list[TaskJoinSource] | None = None +class TaskMessageIdempotency(OrlojModel): + key: str | None = None + message_id: str | None = None + state: str | None = None + updated_at: str | None = None + expires_at: str | None = None + worker: str | None = None + + +class TaskDelegationState(OrlojModel): + attempt: int | None = None + node: str | None = None + mode: str | None = None + expected: int | None = None + quorum_required: int | None = None + activated: bool | None = None + activated_at: str | None = None + activated_by: str | None = None + sources: list[TaskJoinSource] | None = None + + +class TaskBlockedOn(OrlojModel): + kind: str | None = None + name: str | None = None + reason: str | None = None + + class TaskStatus(OrlojModel): phase: str | None = None last_error: str | None = Field(None, alias="lastError") @@ -128,7 +155,10 @@ class TaskStatus(OrlojModel): trace: list[TaskTraceEvent] | None = None history: list[TaskHistoryEvent] | None = None messages: list[TaskMessage] | None = None + message_idempotency: list[TaskMessageIdempotency] | None = None join_states: list[TaskJoinState] | None = None + delegation_states: list[TaskDelegationState] | None = None + blocked_on: TaskBlockedOn | None = None observed_generation: int | None = Field(None, alias="observedGeneration") diff --git a/src/orloj_sdk/models/task_approval.py b/src/orloj_sdk/models/task_approval.py new file mode 100644 index 0000000..d3fc0cf --- /dev/null +++ b/src/orloj_sdk/models/task_approval.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import Field + +from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel + + +class TaskApprovalSpec(OrlojModel): + task_ref: str + checkpoint_id: str + checkpoint_type: str | None = None + agent: str | None = None + reason: str | None = None + ttl: str | None = None + allow_request_changes: bool | None = None + max_review_cycles: int | None = None + review_cycle: int | None = None + supersedes: str | None = None + output: str | None = None + output_format: str | None = None + resume_context: dict[str, Any] | None = None + + +class TaskApprovalStatus(OrlojModel): + phase: str | None = None + decision: str | None = None + decided_by: str | None = None + decided_at: str | None = None + comment: str | None = None + expires_at: str | None = None + + +class TaskApproval(OrlojModel): + api_version: str = Field("orloj.dev/v1", alias="apiVersion") + kind: str = "TaskApproval" + metadata: ObjectMeta + spec: TaskApprovalSpec + status: TaskApprovalStatus | None = None + + +class TaskApprovalList(OrlojModel): + metadata: ListMeta | None = None + items: list[TaskApproval] diff --git a/src/orloj_sdk/models/task_schedule.py b/src/orloj_sdk/models/task_schedule.py index e1d76e0..54cf324 100644 --- a/src/orloj_sdk/models/task_schedule.py +++ b/src/orloj_sdk/models/task_schedule.py @@ -3,10 +3,12 @@ from pydantic import Field from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel +from orloj_sdk.models.task import TaskSpec class TaskScheduleSpec(OrlojModel): task_ref: str | None = None + task_template: TaskSpec | None = None schedule: str | None = None time_zone: str | None = None suspend: bool | None = None diff --git a/src/orloj_sdk/models/task_webhook.py b/src/orloj_sdk/models/task_webhook.py index 5fdd943..2f1d5db 100644 --- a/src/orloj_sdk/models/task_webhook.py +++ b/src/orloj_sdk/models/task_webhook.py @@ -3,6 +3,7 @@ from pydantic import Field from orloj_sdk.models._base import ListMeta, ObjectMeta, OrlojModel +from orloj_sdk.models.task import TaskSpec class TaskWebhookAuthSpec(OrlojModel): @@ -12,10 +13,19 @@ class TaskWebhookAuthSpec(OrlojModel): signature_prefix: str | None = None timestamp_header: str | None = None max_skew_seconds: int | None = None + algorithm: str | None = None + payload_format: str | None = None + payload_prefix: str | None = None + payload_separator: str | None = None + signature_encoding: str | None = None + header_format: str | None = None + signature_key: str | None = None + timestamp_key: str | None = None class TaskWebhookIdempotency(OrlojModel): event_id_header: str | None = None + event_id_from_body: str | None = None dedupe_window_seconds: int | None = None @@ -26,6 +36,7 @@ class TaskWebhookPayloadSpec(OrlojModel): class TaskWebhookSpec(OrlojModel): task_ref: str | None = None + task_template: TaskSpec | None = None suspend: bool | None = None auth: TaskWebhookAuthSpec | None = None idempotency: TaskWebhookIdempotency | None = None diff --git a/src/orloj_sdk/models/tool.py b/src/orloj_sdk/models/tool.py index 1e55ed7..8a57858 100644 --- a/src/orloj_sdk/models/tool.py +++ b/src/orloj_sdk/models/tool.py @@ -13,16 +13,33 @@ class ToolCliEnvRef(OrlojModel): key: str | None = None +class ContainerResources(OrlojModel): + memory: str | None = None + cpus: str | None = None + pids_limit: int | None = None + + class ToolCliSpec(OrlojModel): command: str | None = None args: list[str] | None = None image: str | None = None + image_pull_secret: str | None = None network: str | None = None stdin_from_input: bool | None = None output: str | None = None working_dir: str | None = None env: dict[str, str] | None = None env_from: list[ToolCliEnvRef] | None = None + resources: ContainerResources | None = None + + +class ToolWasmSpec(OrlojModel): + module: str | None = None + entrypoint: str | None = None + max_memory_bytes: int | None = None + fuel: int | None = None + enable_wasi: bool | None = None + image_pull_secret: str | None = None class ToolAuth(OrlojModel): @@ -46,6 +63,12 @@ class ToolRuntimePolicy(OrlojModel): retry: ToolRetryPolicy | None = None +class ToolA2ASpec(OrlojModel): + agent_url: str | None = None + protocol_version: str | None = None + prefer_streaming: bool | None = None + + class ToolSpec(OrlojModel): type: str | None = None endpoint: str | None = None @@ -54,11 +77,13 @@ class ToolSpec(OrlojModel): mcp_server_ref: str | None = None mcp_tool_name: str | None = None cli: ToolCliSpec | None = None + wasm: ToolWasmSpec | None = None capabilities: list[str] | None = None operation_classes: list[str] | None = None risk_level: str | None = None runtime: ToolRuntimePolicy | None = None auth: ToolAuth | None = None + a2a: ToolA2ASpec | None = None class ToolStatus(OrlojModel): diff --git a/src/orloj_sdk/models/tool_approval.py b/src/orloj_sdk/models/tool_approval.py index e2517ca..dd6501a 100644 --- a/src/orloj_sdk/models/tool_approval.py +++ b/src/orloj_sdk/models/tool_approval.py @@ -20,6 +20,7 @@ class ToolApprovalStatus(OrlojModel): decision: str | None = None decided_by: str | None = None decided_at: str | None = None + comment: str | None = None expires_at: str | None = None diff --git a/src/orloj_sdk/resources/a2a.py b/src/orloj_sdk/resources/a2a.py new file mode 100644 index 0000000..5b0f29e --- /dev/null +++ b/src/orloj_sdk/resources/a2a.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +from typing import Any + +from orloj_sdk.models._base import dump_json_body +from orloj_sdk.models.a2a import ( + A2AJsonRpcRequest, + A2AJsonRpcResponse, + A2ARegistryResponse, + AgentCard, +) +from orloj_sdk.resources._base import _path_seg + + +class A2A: + def __init__(self, client: Any) -> None: + self._client = client + + def _ns(self, namespace: str | None) -> str: + return namespace if namespace is not None else self._client._namespace + + def list_agents(self, *, namespace: str | None = None) -> A2ARegistryResponse: + r = self._client._request( + "GET", + "/v1/a2a/agents", + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return A2ARegistryResponse.model_validate(r.json()) + + def get_system_card( + self, + name: str, + *, + namespace: str | None = None, + ) -> AgentCard: + p = f"/v1/agent-systems/{_path_seg(name)}/.well-known/agent-card.json" + r = self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + def get_agent_card( + self, + name: str, + *, + namespace: str | None = None, + ) -> AgentCard: + p = f"/v1/agents/{_path_seg(name)}/.well-known/agent-card.json" + r = self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + def get_default_card(self) -> AgentCard: + r = self._client._request("GET", "/.well-known/agent-card.json") + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + def invoke( + self, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + r = self._client._request( + "POST", + "/a2a", + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) + + def invoke_system( + self, + name: str, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + p = f"/v1/agent-systems/{_path_seg(name)}/a2a" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) + + def invoke_agent( + self, + name: str, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + p = f"/v1/agents/{_path_seg(name)}/a2a" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) + + +class AsyncA2A: + def __init__(self, client: Any) -> None: + self._client = client + + def _ns(self, namespace: str | None) -> str: + return namespace if namespace is not None else self._client._namespace + + async def list_agents(self, *, namespace: str | None = None) -> A2ARegistryResponse: + r = await self._client._request( + "GET", + "/v1/a2a/agents", + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return A2ARegistryResponse.model_validate(r.json()) + + async def get_system_card( + self, + name: str, + *, + namespace: str | None = None, + ) -> AgentCard: + p = f"/v1/agent-systems/{_path_seg(name)}/.well-known/agent-card.json" + r = await self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + async def get_agent_card( + self, + name: str, + *, + namespace: str | None = None, + ) -> AgentCard: + p = f"/v1/agents/{_path_seg(name)}/.well-known/agent-card.json" + r = await self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + async def get_default_card(self) -> AgentCard: + r = await self._client._request("GET", "/.well-known/agent-card.json") + self._client._check_response(r) + return AgentCard.model_validate(r.json()) + + async def invoke( + self, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + r = await self._client._request( + "POST", + "/a2a", + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) + + async def invoke_system( + self, + name: str, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + p = f"/v1/agent-systems/{_path_seg(name)}/a2a" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) + + async def invoke_agent( + self, + name: str, + request: A2AJsonRpcRequest | dict[str, Any], + *, + namespace: str | None = None, + ) -> A2AJsonRpcResponse: + body = dump_json_body(request) if isinstance(request, A2AJsonRpcRequest) else request + p = f"/v1/agents/{_path_seg(name)}/a2a" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=body, + ) + self._client._check_response(r) + return A2AJsonRpcResponse.model_validate(r.json()) diff --git a/src/orloj_sdk/resources/auth.py b/src/orloj_sdk/resources/auth.py index 046714f..b68c193 100644 --- a/src/orloj_sdk/resources/auth.py +++ b/src/orloj_sdk/resources/auth.py @@ -3,6 +3,7 @@ from typing import Any, cast from orloj_sdk.models.auth import TokenInfo, WhoAmI +from orloj_sdk.models.observability import CLITokenResponse from orloj_sdk.resources._base import _path_seg @@ -76,6 +77,15 @@ def login(self, username: str, password: str) -> dict[str, Any]: self._client._check_response(r) return cast("dict[str, Any]", r.json()) + def cli_token(self, username: str, password: str) -> CLITokenResponse: + r = self._client._request( + "POST", + "/v1/auth/cli-token", + json={"username": username, "password": password}, + ) + self._client._check_response(r) + return CLITokenResponse.model_validate(r.json()) + def logout(self) -> None: r = self._client._request("POST", "/v1/auth/logout") self._client._check_response(r) @@ -189,6 +199,15 @@ async def login(self, username: str, password: str) -> dict[str, Any]: self._client._check_response(r) return cast("dict[str, Any]", r.json()) + async def cli_token(self, username: str, password: str) -> CLITokenResponse: + r = await self._client._request( + "POST", + "/v1/auth/cli-token", + json={"username": username, "password": password}, + ) + self._client._check_response(r) + return CLITokenResponse.model_validate(r.json()) + async def logout(self) -> None: r = await self._client._request("POST", "/v1/auth/logout") self._client._check_response(r) diff --git a/src/orloj_sdk/resources/context_adapters.py b/src/orloj_sdk/resources/context_adapters.py new file mode 100644 index 0000000..c23a83d --- /dev/null +++ b/src/orloj_sdk/resources/context_adapters.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from orloj_sdk.models.context_adapter import ContextAdapter, ContextAdapterList +from orloj_sdk.resources._base import AsyncBaseResource, BaseResource + + +class ContextAdapters(BaseResource[ContextAdapter, ContextAdapterList]): + path = "/v1/context-adapters" + item_model = ContextAdapter + list_model = ContextAdapterList + + +class AsyncContextAdapters(AsyncBaseResource[ContextAdapter, ContextAdapterList]): + path = "/v1/context-adapters" + item_model = ContextAdapter + list_model = ContextAdapterList diff --git a/src/orloj_sdk/resources/eval_datasets.py b/src/orloj_sdk/resources/eval_datasets.py new file mode 100644 index 0000000..6bcecb0 --- /dev/null +++ b/src/orloj_sdk/resources/eval_datasets.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from orloj_sdk.models.eval import EvalDataset, EvalDatasetList +from orloj_sdk.resources._base import AsyncBaseResource, BaseResource + + +class EvalDatasets(BaseResource[EvalDataset, EvalDatasetList]): + path = "/v1/eval-datasets" + item_model = EvalDataset + list_model = EvalDatasetList + + +class AsyncEvalDatasets(AsyncBaseResource[EvalDataset, EvalDatasetList]): + path = "/v1/eval-datasets" + item_model = EvalDataset + list_model = EvalDatasetList diff --git a/src/orloj_sdk/resources/eval_runs.py b/src/orloj_sdk/resources/eval_runs.py new file mode 100644 index 0000000..618909d --- /dev/null +++ b/src/orloj_sdk/resources/eval_runs.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +from typing import Any + +from orloj_sdk.models._base import dump_json_body +from orloj_sdk.models.eval import ( + EvalRun, + EvalRunCompareResponse, + EvalRunList, + EvalSampleAnnotation, + EvalSampleResult, +) +from orloj_sdk.resources._base import AsyncBaseResource, BaseResource, _path_seg + + +class EvalRuns(BaseResource[EvalRun, EvalRunList]): + path = "/v1/eval-runs" + item_model = EvalRun + list_model = EvalRunList + + def start(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/start" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + def cancel(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/cancel" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + def finalize(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/finalize" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + def export( + self, + name: str, + *, + namespace: str | None = None, + format: str = "json", + ) -> list[EvalSampleResult] | str: + p = f"{self.path}/{_path_seg(name)}/export" + r = self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace), "format": format}, + ) + self._client._check_response(r) + if format == "csv": + return str(r.text) + data = r.json() + if not isinstance(data, list): + return [] + return [EvalSampleResult.model_validate(x) for x in data] + + def annotate_sample( + self, + name: str, + sample_name: str, + annotation: EvalSampleAnnotation, + *, + namespace: str | None = None, + ) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/results/{_path_seg(sample_name)}" + r = self._client._request( + "PUT", + p, + params={"namespace": self._ns(namespace)}, + json=dump_json_body(annotation), + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + def import_annotations( + self, + name: str, + annotations: list[dict[str, Any]], + *, + namespace: str | None = None, + ) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/results" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=annotations, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + def compare( + self, + runs: list[str], + *, + namespace: str | None = None, + ) -> EvalRunCompareResponse: + r = self._client._request( + "GET", + f"{self.path}/compare", + params={"namespace": self._ns(namespace), "runs": ",".join(runs)}, + ) + self._client._check_response(r) + return EvalRunCompareResponse.model_validate(r.json()) + + +class AsyncEvalRuns(AsyncBaseResource[EvalRun, EvalRunList]): + path = "/v1/eval-runs" + item_model = EvalRun + list_model = EvalRunList + + async def start(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/start" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + async def cancel(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/cancel" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + async def finalize(self, name: str, *, namespace: str | None = None) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/finalize" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + async def export( + self, + name: str, + *, + namespace: str | None = None, + format: str = "json", + ) -> list[EvalSampleResult] | str: + p = f"{self.path}/{_path_seg(name)}/export" + r = await self._client._request( + "GET", + p, + params={"namespace": self._ns(namespace), "format": format}, + ) + self._client._check_response(r) + if format == "csv": + return str(r.text) + data = r.json() + if not isinstance(data, list): + return [] + return [EvalSampleResult.model_validate(x) for x in data] + + async def annotate_sample( + self, + name: str, + sample_name: str, + annotation: EvalSampleAnnotation, + *, + namespace: str | None = None, + ) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/results/{_path_seg(sample_name)}" + r = await self._client._request( + "PUT", + p, + params={"namespace": self._ns(namespace)}, + json=dump_json_body(annotation), + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + async def import_annotations( + self, + name: str, + annotations: list[dict[str, Any]], + *, + namespace: str | None = None, + ) -> EvalRun: + p = f"{self.path}/{_path_seg(name)}/results" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=annotations, + ) + self._client._check_response(r) + return EvalRun.model_validate(r.json()) + + async def compare( + self, + runs: list[str], + *, + namespace: str | None = None, + ) -> EvalRunCompareResponse: + r = await self._client._request( + "GET", + f"{self.path}/compare", + params={"namespace": self._ns(namespace), "runs": ",".join(runs)}, + ) + self._client._check_response(r) + return EvalRunCompareResponse.model_validate(r.json()) diff --git a/src/orloj_sdk/resources/sealed_secrets.py b/src/orloj_sdk/resources/sealed_secrets.py new file mode 100644 index 0000000..82639f6 --- /dev/null +++ b/src/orloj_sdk/resources/sealed_secrets.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from orloj_sdk.models.sealed_secret import ( + SealedSecret, + SealedSecretList, + SealedSecretPublicKeyResponse, +) +from orloj_sdk.resources._base import AsyncBaseResource, BaseResource + + +class SealedSecrets(BaseResource[SealedSecret, SealedSecretList]): + path = "/v1/sealed-secrets" + item_model = SealedSecret + list_model = SealedSecretList + + def get_public_key(self) -> SealedSecretPublicKeyResponse: + r = self._client._request("GET", "/v1/sealing-key/public") + self._client._check_response(r) + return SealedSecretPublicKeyResponse.model_validate(r.json()) + + +class AsyncSealedSecrets(AsyncBaseResource[SealedSecret, SealedSecretList]): + path = "/v1/sealed-secrets" + item_model = SealedSecret + list_model = SealedSecretList + + async def get_public_key(self) -> SealedSecretPublicKeyResponse: + r = await self._client._request("GET", "/v1/sealing-key/public") + self._client._check_response(r) + return SealedSecretPublicKeyResponse.model_validate(r.json()) diff --git a/src/orloj_sdk/resources/task_approvals.py b/src/orloj_sdk/resources/task_approvals.py new file mode 100644 index 0000000..23aa685 --- /dev/null +++ b/src/orloj_sdk/resources/task_approvals.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +from typing import Any + +from orloj_sdk.models.task_approval import TaskApproval, TaskApprovalList +from orloj_sdk.resources._base import AsyncBaseResource, BaseResource, _path_seg + + +def _decision_body( + *, + decided_by: str | None, + comment: str | None, + reason: str | None, +) -> dict[str, Any] | None: + if decided_by is None and comment is None and reason is None: + return None + body: dict[str, Any] = {} + if decided_by is not None: + body["decided_by"] = decided_by + if comment is not None: + body["comment"] = comment + if reason is not None: + body["reason"] = reason + return body + + +class TaskApprovals(BaseResource[TaskApproval, TaskApprovalList]): + path = "/v1/task-approvals" + item_model = TaskApproval + list_model = TaskApprovalList + supports_status = False + + def approve( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str | None = None, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/approve" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) + + def deny( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str | None = None, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/deny" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) + + def request_changes( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/request-changes" + r = self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) + + +class AsyncTaskApprovals(AsyncBaseResource[TaskApproval, TaskApprovalList]): + path = "/v1/task-approvals" + item_model = TaskApproval + list_model = TaskApprovalList + supports_status = False + + async def approve( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str | None = None, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/approve" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) + + async def deny( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str | None = None, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/deny" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) + + async def request_changes( + self, + name: str, + *, + namespace: str | None = None, + decided_by: str | None = None, + comment: str, + reason: str | None = None, + ) -> TaskApproval: + p = f"{self.path}/{_path_seg(name)}/request-changes" + r = await self._client._request( + "POST", + p, + params={"namespace": self._ns(namespace)}, + json=_decision_body(decided_by=decided_by, comment=comment, reason=reason), + ) + self._client._check_response(r) + return TaskApproval.model_validate(r.json()) diff --git a/src/orloj_sdk/resources/tasks.py b/src/orloj_sdk/resources/tasks.py index a1f9d8e..09a1980 100644 --- a/src/orloj_sdk/resources/tasks.py +++ b/src/orloj_sdk/resources/tasks.py @@ -1,12 +1,30 @@ from __future__ import annotations -from typing import Any +import asyncio +import time +from typing import Any, TypeVar, overload + +from pydantic import BaseModel from orloj_sdk._streaming import AsyncWatchContext, WatchStream +from orloj_sdk._tasks import ( + DEFAULT_RUN_POLL_SECONDS, + DEFAULT_RUN_TIMEOUT_SECONDS, + TaskFailedError, + TaskTimeoutError, + coerce_task_body, + is_success_phase, + is_terminal_phase, + parse_task_output, + task_phase, +) from orloj_sdk.models.auth import NamedLogsResponse +from orloj_sdk.models.observability import TaskMessageListResponse, TaskMessageMetricsResponse from orloj_sdk.models.task import Task, TaskList from orloj_sdk.resources._base import AsyncBaseResource, BaseResource, _path_seg +T = TypeVar("T", bound=BaseModel) + class Tasks(BaseResource[Task, TaskList]): path = "/v1/tasks" @@ -19,6 +37,63 @@ def get_logs(self, name: str, *, namespace: str | None = None) -> NamedLogsRespo self._client._check_response(r) return NamedLogsResponse.model_validate(r.json()) + def get_messages( + self, + name: str, + *, + namespace: str | None = None, + phase: str | None = None, + from_agent: str | None = None, + to_agent: str | None = None, + branch_id: str | None = None, + trace_id: str | None = None, + max_results: int | None = None, + ) -> TaskMessageListResponse: + p = f"{self.path}/{_path_seg(name)}/messages" + params: dict[str, str | int] = {"namespace": self._ns(namespace)} + if phase is not None: + params["phase"] = phase + if from_agent is not None: + params["from_agent"] = from_agent + if to_agent is not None: + params["to_agent"] = to_agent + if branch_id is not None: + params["branch_id"] = branch_id + if trace_id is not None: + params["trace_id"] = trace_id + if max_results is not None: + params["max_results"] = max_results + r = self._client._request("GET", p, params=params) + self._client._check_response(r) + return TaskMessageListResponse.model_validate(r.json()) + + def get_metrics( + self, + name: str, + *, + namespace: str | None = None, + phase: str | None = None, + from_agent: str | None = None, + to_agent: str | None = None, + branch_id: str | None = None, + trace_id: str | None = None, + ) -> TaskMessageMetricsResponse: + p = f"{self.path}/{_path_seg(name)}/metrics" + params: dict[str, str] = {"namespace": self._ns(namespace)} + if phase is not None: + params["phase"] = phase + if from_agent is not None: + params["from_agent"] = from_agent + if to_agent is not None: + params["to_agent"] = to_agent + if branch_id is not None: + params["branch_id"] = branch_id + if trace_id is not None: + params["trace_id"] = trace_id + r = self._client._request("GET", p, params=params) + self._client._check_response(r) + return TaskMessageMetricsResponse.model_validate(r.json()) + def cancel( self, name: str, @@ -68,6 +143,115 @@ def watch( r, stack = self._client._open_stream("GET", f"{self.path}/watch", params=params) return WatchStream(r, Task, close_stack=stack) + def wait( + self, + name: str, + *, + namespace: str | None = None, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + ) -> Task: + """Poll until the task reaches a terminal phase (Succeeded/Failed/DeadLetter).""" + ns = self._ns(namespace) + deadline = time.monotonic() + timeout + task = self.get(name, namespace=ns) + while not is_terminal_phase(task_phase(task)): + if time.monotonic() >= deadline: + raise TaskTimeoutError(name, timeout, task=task) + time.sleep(poll_interval) + task = self.get(name, namespace=ns) + if raise_on_failure and not is_success_phase(task_phase(task)): + raise TaskFailedError(task) + return task + + @overload + def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: None = None, + output_key: str | None = None, + ) -> Task: ... + + @overload + def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: type[T], + output_key: str | None = None, + ) -> T: ... + + def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: type[T] | None = None, + output_key: str | None = None, + ) -> Task | T: + """Create a task and optionally wait for completion. + + Shorthand:: + + result = client.tasks.run( + name="job-1", + system="demo-pipeline", + input={"query": "hello"}, + wait=True, + output_type=MyModel, + ) + """ + ns = self._ns(namespace) + task_body = coerce_task_body( + body, + name=name, + system=system, + input=input, + namespace=ns, + ) + created = self.create(task_body, namespace=ns) + if not wait: + return created + finished = self.wait( + created.metadata.name, + namespace=ns, + timeout=timeout, + poll_interval=poll_interval, + raise_on_failure=raise_on_failure, + ) + if output_type is not None: + return parse_task_output(finished, output_type, key=output_key) + return finished + + def output(self, task: Task, model_type: type[T], *, key: str | None = None) -> T: + """Parse a finished task's ``status.output`` into ``model_type``.""" + return parse_task_output(task, model_type, key=key) + class AsyncTasks(AsyncBaseResource[Task, TaskList]): path = "/v1/tasks" @@ -80,6 +264,63 @@ async def get_logs(self, name: str, *, namespace: str | None = None) -> NamedLog self._client._check_response(r) return NamedLogsResponse.model_validate(r.json()) + async def get_messages( + self, + name: str, + *, + namespace: str | None = None, + phase: str | None = None, + from_agent: str | None = None, + to_agent: str | None = None, + branch_id: str | None = None, + trace_id: str | None = None, + max_results: int | None = None, + ) -> TaskMessageListResponse: + p = f"{self.path}/{_path_seg(name)}/messages" + params: dict[str, str | int] = {"namespace": self._ns(namespace)} + if phase is not None: + params["phase"] = phase + if from_agent is not None: + params["from_agent"] = from_agent + if to_agent is not None: + params["to_agent"] = to_agent + if branch_id is not None: + params["branch_id"] = branch_id + if trace_id is not None: + params["trace_id"] = trace_id + if max_results is not None: + params["max_results"] = max_results + r = await self._client._request("GET", p, params=params) + self._client._check_response(r) + return TaskMessageListResponse.model_validate(r.json()) + + async def get_metrics( + self, + name: str, + *, + namespace: str | None = None, + phase: str | None = None, + from_agent: str | None = None, + to_agent: str | None = None, + branch_id: str | None = None, + trace_id: str | None = None, + ) -> TaskMessageMetricsResponse: + p = f"{self.path}/{_path_seg(name)}/metrics" + params: dict[str, str] = {"namespace": self._ns(namespace)} + if phase is not None: + params["phase"] = phase + if from_agent is not None: + params["from_agent"] = from_agent + if to_agent is not None: + params["to_agent"] = to_agent + if branch_id is not None: + params["branch_id"] = branch_id + if trace_id is not None: + params["trace_id"] = trace_id + r = await self._client._request("GET", p, params=params) + self._client._check_response(r) + return TaskMessageMetricsResponse.model_validate(r.json()) + async def cancel( self, name: str, @@ -127,3 +368,98 @@ def watch( if resource_version is not None: params["resourceVersion"] = resource_version return AsyncWatchContext(self._client, f"{self.path}/watch", params, Task) + + async def wait( + self, + name: str, + *, + namespace: str | None = None, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + ) -> Task: + ns = self._ns(namespace) + deadline = time.monotonic() + timeout + task = await self.get(name, namespace=ns) + while not is_terminal_phase(task_phase(task)): + if time.monotonic() >= deadline: + raise TaskTimeoutError(name, timeout, task=task) + await asyncio.sleep(poll_interval) + task = await self.get(name, namespace=ns) + if raise_on_failure and not is_success_phase(task_phase(task)): + raise TaskFailedError(task) + return task + + @overload + async def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: None = None, + output_key: str | None = None, + ) -> Task: ... + + @overload + async def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: type[T], + output_key: str | None = None, + ) -> T: ... + + async def run( + self, + body: Task | None = None, + *, + name: str | None = None, + system: str | None = None, + input: dict[str, str] | None = None, + namespace: str | None = None, + wait: bool = True, + timeout: float = DEFAULT_RUN_TIMEOUT_SECONDS, + poll_interval: float = DEFAULT_RUN_POLL_SECONDS, + raise_on_failure: bool = True, + output_type: type[T] | None = None, + output_key: str | None = None, + ) -> Task | T: + ns = self._ns(namespace) + task_body = coerce_task_body( + body, + name=name, + system=system, + input=input, + namespace=ns, + ) + created = await self.create(task_body, namespace=ns) + if not wait: + return created + finished = await self.wait( + created.metadata.name, + namespace=ns, + timeout=timeout, + poll_interval=poll_interval, + raise_on_failure=raise_on_failure, + ) + if output_type is not None: + return parse_task_output(finished, output_type, key=output_key) + return finished + + def output(self, task: Task, model_type: type[T], *, key: str | None = None) -> T: + return parse_task_output(task, model_type, key=key) diff --git a/src/orloj_sdk/resources/tool_approvals.py b/src/orloj_sdk/resources/tool_approvals.py index 6f7f5ee..131bc8e 100644 --- a/src/orloj_sdk/resources/tool_approvals.py +++ b/src/orloj_sdk/resources/tool_approvals.py @@ -25,7 +25,7 @@ def approve( if decided_by is not None or reason is not None: body = {} if decided_by is not None: - body["decidedBy"] = decided_by + body["decided_by"] = decided_by if reason is not None: body["reason"] = reason r = self._client._request( @@ -50,7 +50,7 @@ def deny( if decided_by is not None or reason is not None: body = {} if decided_by is not None: - body["decidedBy"] = decided_by + body["decided_by"] = decided_by if reason is not None: body["reason"] = reason r = self._client._request( @@ -82,7 +82,7 @@ async def approve( if decided_by is not None or reason is not None: body = {} if decided_by is not None: - body["decidedBy"] = decided_by + body["decided_by"] = decided_by if reason is not None: body["reason"] = reason r = await self._client._request( @@ -107,7 +107,7 @@ async def deny( if decided_by is not None or reason is not None: body = {} if decided_by is not None: - body["decidedBy"] = decided_by + body["decided_by"] = decided_by if reason is not None: body["reason"] = reason r = await self._client._request( diff --git a/tests/test_authoring.py b/tests/test_authoring.py new file mode 100644 index 0000000..344540e --- /dev/null +++ b/tests/test_authoring.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import respx +from httpx import Response + +from orloj_sdk import OrlojClient +from orloj_sdk.authoring import agent, apply, edge, node, system + + +def test_agent_decorator_uses_docstring() -> None: + @agent("researcher", model_ref="claude", tools=["web_search"]) + def researcher() -> None: + """Research the topic and summarize findings.""" + + assert researcher.name == "researcher" + assert researcher.model_ref == "claude" + assert researcher.prompt == "Research the topic and summarize findings." + res = researcher.to_resource() + assert res.metadata.name == "researcher" + assert res.spec.tools == ["web_search"] + + +def test_system_builder_graph() -> None: + researcher = agent("researcher", model_ref="m1")(lambda: None) + writer = agent("writer", model_ref="m1")(lambda: None) + builder = ( + system("pipeline") + .add_agent(researcher.with_prompt("research")) + .add_agent(writer.with_prompt("write")) + .set_graph( + { + "researcher": node(edges=[edge("writer")]), + "writer": node(), + } + ) + ) + sys_res = builder.to_system_resource() + assert sys_res.spec.agents == ["researcher", "writer"] + assert sys_res.spec.graph is not None + assert sys_res.spec.graph["researcher"].edges is not None + assert sys_res.spec.graph["researcher"].edges[0].to == "writer" + + +@respx.mock +def test_apply_creates_agents_and_system(base_url: str) -> None: + agent_body = { + "apiVersion": "orloj.dev/v1", + "kind": "Agent", + "metadata": {"name": "a1", "namespace": "default"}, + "spec": {"model_ref": "m1", "prompt": "hi"}, + } + system_body = { + "apiVersion": "orloj.dev/v1", + "kind": "AgentSystem", + "metadata": {"name": "pipe", "namespace": "default"}, + "spec": {"agents": ["a1"], "graph": {"a1": {}}}, + } + respx.post(f"{base_url}/v1/agents", params={"namespace": "default"}).mock( + return_value=Response(201, json=agent_body) + ) + respx.post(f"{base_url}/v1/agent-systems", params={"namespace": "default"}).mock( + return_value=Response(201, json=system_body) + ) + client = OrlojClient(base_url=base_url, api_token="x") + a = agent("a1", model_ref="m1", prompt="hi")(lambda: None) + builder = system("pipe").add_agent(a) + created = apply(client, builder) + assert len(created) == 1 + assert created[0].metadata.name == "pipe" diff --git a/tests/test_models/test_parity_fields.py b/tests/test_models/test_parity_fields.py new file mode 100644 index 0000000..6399049 --- /dev/null +++ b/tests/test_models/test_parity_fields.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from orloj_sdk.models._base import ObjectMeta +from orloj_sdk.models.agent import Agent, AgentSpec +from orloj_sdk.models.agent_system import AgentSystem, AgentSystemSpec +from orloj_sdk.models.eval import EvalSampleResult +from orloj_sdk.models.mcp_server import McpServer, McpServerSpec + + +def test_agent_parses_fallback_and_output_schema() -> None: + agent = Agent.model_validate( + { + "apiVersion": "orloj.dev/v1", + "kind": "Agent", + "metadata": {"name": "a", "namespace": "default"}, + "spec": { + "model_ref": "m1", + "fallback_model_refs": ["m2"], + "execution": {"output_schema": {"type": "object"}}, + }, + } + ) + assert agent.spec.fallback_model_refs == ["m2"] + assert agent.spec.execution is not None + assert agent.spec.execution.output_schema == {"type": "object"} + + +def test_agent_system_parses_a2a_and_delegation() -> None: + system = AgentSystem.model_validate( + { + "apiVersion": "orloj.dev/v1", + "kind": "AgentSystem", + "metadata": {"name": "sys", "namespace": "default"}, + "spec": { + "agents": ["manager"], + "context_adapter": "sanitize", + "a2a": {"enabled": True, "auth": "bearer"}, + "graph": { + "manager": { + "delegates": [{"to": "worker"}], + "delegate_join": {"mode": "wait_for_all"}, + "review": {"checkpoint_id": "r1"}, + } + }, + }, + } + ) + assert system.spec.a2a is not None + assert system.spec.a2a.enabled is True + assert system.spec.graph is not None + assert system.spec.graph["manager"].delegates is not None + assert system.spec.graph["manager"].delegates[0].to == "worker" + + +def test_mcp_server_parses_allow_private() -> None: + server = McpServer.model_validate( + { + "apiVersion": "orloj.dev/v1", + "kind": "McpServer", + "metadata": {"name": "mcp", "namespace": "default"}, + "spec": { + "transport": "http", + "endpoint": "http://10.0.0.5:8080", + "allowPrivate": True, + "idle_timeout": "5m", + }, + } + ) + assert server.spec.allow_private is True + assert server.spec.idle_timeout == "5m" + + +def test_eval_sample_result_pass_alias() -> None: + result = EvalSampleResult.model_validate({"sample_name": "s1", "pass": True, "score": 0.9}) + assert result.passed is True + dumped = result.model_dump(by_alias=True) + assert dumped["pass"] is True + + +def test_agent_roundtrip_dump() -> None: + agent = Agent( + metadata=ObjectMeta(name="a"), + spec=AgentSpec(model_ref="m1", fallback_model_refs=["m2"]), + ) + payload = agent.model_dump(by_alias=True, exclude_none=True) + assert payload["apiVersion"] == "orloj.dev/v1" + assert payload["spec"]["fallback_model_refs"] == ["m2"] + + +def test_agent_system_spec_construct() -> None: + from orloj_sdk.models.agent_system import AgentSystemA2ASpec + + spec = AgentSystemSpec(agents=["a"], a2a=AgentSystemA2ASpec(enabled=True)) + assert spec.agents == ["a"] + assert spec.a2a is not None + assert spec.a2a.enabled is True + + +def test_mcp_server_spec_construct() -> None: + spec = McpServerSpec(transport="stdio", command="npx", allow_private=False) + assert spec.transport == "stdio" diff --git a/tests/test_resources/test_urls.py b/tests/test_resources/test_urls.py index 7abe4ea..3564080 100644 --- a/tests/test_resources/test_urls.py +++ b/tests/test_resources/test_urls.py @@ -6,6 +6,7 @@ from httpx import Response from orloj_sdk import OrlojClient +from orloj_sdk.models.eval import EvalSampleAnnotation @respx.mock @@ -40,3 +41,212 @@ def test_auth_create_token_json(base_url: str) -> None: client = OrlojClient(base_url=base_url, api_token="x") info = client.auth.create_token("tok", "admin") assert info.token == "secret" + + +@respx.mock +def test_auth_cli_token(base_url: str) -> None: + respx.post(f"{base_url}/v1/auth/cli-token").mock( + return_value=Response( + 201, + json={ + "name": "cli-admin-1", + "role": "admin", + "token": "raw-token", + "username": "admin", + "created_at": "2026-01-01T00:00:00Z", + }, + ) + ) + client = OrlojClient(base_url=base_url, api_token="x") + tok = client.auth.cli_token("admin", "secret") + assert tok.token == "raw-token" + assert tok.username == "admin" + + +@respx.mock +def test_task_approvals_request_changes(base_url: str) -> None: + body = { + "apiVersion": "orloj.dev/v1", + "kind": "TaskApproval", + "metadata": {"name": "ta-1", "namespace": "default"}, + "spec": {"task_ref": "t1", "checkpoint_id": "c1"}, + "status": {"phase": "ChangesRequested", "decision": "request_changes"}, + } + respx.post( + f"{base_url}/v1/task-approvals/ta-1/request-changes", + params={"namespace": "default"}, + ).mock(return_value=Response(200, json=body)) + client = OrlojClient(base_url=base_url, api_token="x") + result = client.task_approvals.request_changes( + "ta-1", + comment="please revise", + decided_by="reviewer", + ) + assert result.status is not None + assert result.status.phase == "ChangesRequested" + + +@respx.mock +def test_sealed_secrets_public_key(base_url: str) -> None: + respx.get(f"{base_url}/v1/sealing-key/public").mock( + return_value=Response( + 200, + json={ + "keyId": "k1", + "algorithm": "RSA-OAEP", + "publicKeyPEM": "-----BEGIN PUBLIC KEY-----\nMIIB\n-----END PUBLIC KEY-----\n", + }, + ) + ) + client = OrlojClient(base_url=base_url, api_token="x") + key = client.sealed_secrets.get_public_key() + assert key.key_id == "k1" + + +@respx.mock +def test_context_adapters_get(base_url: str) -> None: + body = { + "apiVersion": "orloj.dev/v1", + "kind": "ContextAdapter", + "metadata": {"name": "sanitize", "namespace": "default"}, + "spec": {"tool_ref": "sanitize-tool", "on_error": "reject"}, + } + respx.get( + f"{base_url}/v1/context-adapters/sanitize", + params={"namespace": "default"}, + ).mock(return_value=Response(200, json=body)) + client = OrlojClient(base_url=base_url, api_token="x") + adapter = client.context_adapters.get("sanitize") + assert adapter.spec.tool_ref == "sanitize-tool" + + +@respx.mock +def test_eval_runs_start_and_compare(base_url: str) -> None: + run_body = { + "apiVersion": "orloj.dev/v1", + "kind": "EvalRun", + "metadata": {"name": "run-a", "namespace": "default"}, + "spec": {"dataset_ref": "ds", "system": "sys"}, + "status": {"phase": "Running"}, + } + respx.post( + f"{base_url}/v1/eval-runs/run-a/start", + params={"namespace": "default"}, + ).mock(return_value=Response(200, json=run_body)) + respx.get( + f"{base_url}/v1/eval-runs/compare", + params={"namespace": "default", "runs": "run-a,run-b"}, + ).mock( + return_value=Response( + 200, + json={ + "runs": ["run-a", "run-b"], + "summary": {"run-a": {"pass_rate": 1.0, "mean_score": 1.0, "total_tokens": 10}}, + "samples": [], + }, + ) + ) + client = OrlojClient(base_url=base_url, api_token="x") + started = client.eval_runs.start("run-a") + assert started.status is not None + assert started.status.phase == "Running" + compared = client.eval_runs.compare(["run-a", "run-b"]) + assert compared.runs == ["run-a", "run-b"] + + +@respx.mock +def test_eval_runs_annotate_sample(base_url: str) -> None: + run_body = { + "apiVersion": "orloj.dev/v1", + "kind": "EvalRun", + "metadata": {"name": "run-a", "namespace": "default"}, + "spec": {"dataset_ref": "ds", "system": "sys"}, + "status": { + "phase": "PendingReview", + "results": [{"sample_name": "s1", "pass": True, "score": 1.0}], + }, + } + respx.put( + f"{base_url}/v1/eval-runs/run-a/results/s1", + params={"namespace": "default"}, + ).mock(return_value=Response(200, json=run_body)) + client = OrlojClient(base_url=base_url, api_token="x") + updated = client.eval_runs.annotate_sample( + "run-a", + "s1", + EvalSampleAnnotation(passed=True, score=1.0, comment="ok"), + ) + assert updated.status is not None + assert updated.status.results is not None + assert updated.status.results[0].passed is True + + +@respx.mock +def test_tasks_messages_and_metrics(base_url: str) -> None: + respx.get( + f"{base_url}/v1/tasks/t1/messages", + params={"namespace": "default"}, + ).mock( + return_value=Response( + 200, + json={ + "name": "t1", + "namespace": "default", + "total": 1, + "messages": [{"message_id": "m1", "content": "hi"}], + }, + ) + ) + respx.get( + f"{base_url}/v1/tasks/t1/metrics", + params={"namespace": "default"}, + ).mock( + return_value=Response( + 200, + json={ + "name": "t1", + "namespace": "default", + "totals": {"messages": 1, "succeeded": 1}, + "per_agent": [], + "per_edge": [], + }, + ) + ) + client = OrlojClient(base_url=base_url, api_token="x") + messages = client.tasks.get_messages("t1") + assert messages.total == 1 + metrics = client.tasks.get_metrics("t1") + assert metrics.totals is not None + assert metrics.totals.messages == 1 + + +@respx.mock +def test_a2a_registry_and_invoke(base_url: str) -> None: + respx.get( + f"{base_url}/v1/a2a/agents", + params={"namespace": "default"}, + ).mock( + return_value=Response( + 200, + json={ + "localAgents": [{"name": "sys", "url": "http://example/a2a"}], + "remoteAgents": [], + }, + ) + ) + respx.post( + f"{base_url}/a2a", + params={"namespace": "default"}, + ).mock( + return_value=Response( + 200, + json={"jsonrpc": "2.0", "id": "1", "result": {"id": "task-1"}}, + ) + ) + client = OrlojClient(base_url=base_url, api_token="x") + registry = client.a2a.list_agents() + assert registry.local_agents is not None + assert registry.local_agents[0].name == "sys" + resp = client.a2a.invoke({"jsonrpc": "2.0", "method": "tasks/get", "id": "1", "params": {}}) + assert resp.result is not None + assert resp.result["id"] == "task-1" diff --git a/tests/test_tasks_run.py b/tests/test_tasks_run.py new file mode 100644 index 0000000..c6256a9 --- /dev/null +++ b/tests/test_tasks_run.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import respx +from httpx import Response +from pydantic import BaseModel + +from orloj_sdk import OrlojClient, TaskFailedError, TaskTimeoutError, parse_task_output +from orloj_sdk.models._base import ObjectMeta +from orloj_sdk.models.task import Task, TaskSpec + + +class Answer(BaseModel): + summary: str + + +def _task_json(name: str, phase: str, output: dict[str, str] | None = None) -> dict: + status: dict = {"phase": phase} + if output is not None: + status["output"] = output + return { + "apiVersion": "orloj.dev/v1", + "kind": "Task", + "metadata": {"name": name, "namespace": "default"}, + "spec": {"system": "sys", "input": {"q": "hi"}}, + "status": status, + } + + +@respx.mock +def test_tasks_run_wait_success(base_url: str) -> None: + created = _task_json("job-1", "Pending") + done = _task_json("job-1", "Succeeded", {"result": '{"summary": "ok"}'}) + respx.post(f"{base_url}/v1/tasks", params={"namespace": "default"}).mock( + return_value=Response(201, json=created) + ) + get = respx.get(f"{base_url}/v1/tasks/job-1", params={"namespace": "default"}) + get.side_effect = [ + Response(200, json=_task_json("job-1", "Running")), + Response(200, json=done), + ] + client = OrlojClient(base_url=base_url, api_token="x") + task = client.tasks.run( + name="job-1", + system="sys", + input={"q": "hi"}, + wait=True, + poll_interval=0.01, + timeout=5, + ) + assert task.status is not None + assert task.status.phase == "Succeeded" + + +@respx.mock +def test_tasks_run_typed_output(base_url: str) -> None: + created = _task_json("job-2", "Pending") + done = _task_json("job-2", "Succeeded", {"result": '{"summary": "typed"}'}) + respx.post(f"{base_url}/v1/tasks", params={"namespace": "default"}).mock( + return_value=Response(201, json=created) + ) + respx.get(f"{base_url}/v1/tasks/job-2", params={"namespace": "default"}).mock( + return_value=Response(200, json=done) + ) + client = OrlojClient(base_url=base_url, api_token="x") + answer = client.tasks.run( + name="job-2", + system="sys", + wait=True, + poll_interval=0.01, + output_type=Answer, + output_key="result", + ) + assert isinstance(answer, Answer) + assert answer.summary == "typed" + + +@respx.mock +def test_tasks_run_failure_raises(base_url: str) -> None: + created = _task_json("job-3", "Pending") + failed = _task_json("job-3", "DeadLetter") + failed["status"]["lastError"] = "boom" + respx.post(f"{base_url}/v1/tasks", params={"namespace": "default"}).mock( + return_value=Response(201, json=created) + ) + respx.get(f"{base_url}/v1/tasks/job-3", params={"namespace": "default"}).mock( + return_value=Response(200, json=failed) + ) + client = OrlojClient(base_url=base_url, api_token="x") + try: + client.tasks.run(name="job-3", system="sys", wait=True, poll_interval=0.01) + raise AssertionError("expected TaskFailedError") + except TaskFailedError as exc: + assert exc.phase == "DeadLetter" + assert "boom" in str(exc) + + +@respx.mock +def test_tasks_wait_timeout(base_url: str) -> None: + respx.get(f"{base_url}/v1/tasks/slow", params={"namespace": "default"}).mock( + return_value=Response(200, json=_task_json("slow", "Running")) + ) + client = OrlojClient(base_url=base_url, api_token="x") + try: + client.tasks.wait("slow", timeout=0.05, poll_interval=0.01) + raise AssertionError("expected TaskTimeoutError") + except TaskTimeoutError as exc: + assert exc.timeout == 0.05 + + +def test_parse_task_output_single_json() -> None: + task = Task( + metadata=ObjectMeta(name="t"), + spec=TaskSpec(system="s"), + status={"phase": "Succeeded", "output": {"out": '{"summary": "x"}'}}, # type: ignore[arg-type] + ) + # Build status properly + from orloj_sdk.models.task import TaskStatus + + task.status = TaskStatus(phase="Succeeded", output={"out": '{"summary": "x"}'}) + parsed = parse_task_output(task, Answer) + assert parsed.summary == "x" + + +def test_client_default_max_retries() -> None: + client = OrlojClient(base_url="http://127.0.0.1:9", api_token="x") + assert client._max_retries == 3 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..b1e6c16 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1153 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version < '3.14'", +] + +[[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.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, +] + +[[package]] +name = "argcomplete" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284, upload-time = "2026-06-30T22:28:22.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575, upload-time = "2026-06-30T22:28:20.547Z" }, +] + +[[package]] +name = "ast-serialize" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489, upload-time = "2026-06-30T20:02:55.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/12/3e5f575f156555547c250a8b0d1347517a3a20fc7f4492e9703a69d4f45e/ast_serialize-0.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a7520b672827885bafeae7501f684d14d47d17e5f45256f9df547686cca52264", size = 1177640, upload-time = "2026-06-30T20:02:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a4/921a9e27951627983b0f368859ea00f8330a551dc0bf4c2fdcb11855a98b/ast_serialize-0.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a14191beec7e0c078d2fc1f6edc0aee88bcd4db9f18e1bc9f8052b559c22dddc", size = 1168111, upload-time = "2026-06-30T20:02:08.366Z" }, + { url = "https://files.pythonhosted.org/packages/00/69/950cf404de7b8782cf95e5c1237e25e2aa46177b287f39f9eeddf481fd6f/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32ef62ec34cf6be20ad77d4799556638fbdf187f3ae10698dfb20ef9f2c89516", size = 1227656, upload-time = "2026-06-30T20:02:09.843Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a8/46f8f6a6479d9d2273980957bb091a506c55f5b95d3c029ee58518a78407/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13b7769970a39983b0adf2f38917b1cd3b8946f76df045756c3d741bc689f089", size = 1227706, upload-time = "2026-06-30T20:02:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/9ac415bda0a40e49eab8fea3b2741c19c98bb84d57d62c4cfc6230eb67be/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f7a408601bb3edaefb3bc67a4c01f5235e3253653b6a5729a2ee2382b35341c", size = 1431705, upload-time = "2026-06-30T20:02:12.737Z" }, + { url = "https://files.pythonhosted.org/packages/e5/06/8807115d441444879f7561b5eede5ac18fc80392f11826d61ccf31f503b1/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8670bfa51208a2c0c8d138928e40e998fab158f9200d53bb80c088b5b8eda7b8", size = 1249533, upload-time = "2026-06-30T20:02:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/c2ba82ef9618650357d9421a1fdb27ffec862a7f57e8e2de82a3ccd11e12/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4826809eb8597a8cd59fd924b6d7c285b8969a1e0007e2cb652cab62376270f", size = 1252619, upload-time = "2026-06-30T20:02:16.219Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a7/fa31d52dd4102cede29fb9634e98d214129b2783b4f95528c6dc6a8f6587/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:577a6c189068686869f5f1ddc38363f3ae1808a4753b577266f9202071a7bb66", size = 1242983, upload-time = "2026-06-30T20:02:17.813Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/ddf742b5ad3c4bafd3466f2265037cfd99bc1b9a5ee46a5d58c90d523242/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085de7f62dc9cc247eb01e965a362707d1d90b1d89a82c5bf78301a60a3c417b", size = 1296148, upload-time = "2026-06-30T20:02:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/24/cb/9f6f217cce8b3b632c5568b478d195a35e79dce4dbe309438cb89ba6ea4f/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9f8a8b78b13173de6a9ec22111d9be674874cd5bdccda04f14ae5ebc2bef403a", size = 1403826, upload-time = "2026-06-30T20:02:20.696Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f8/9d16d4f0107a183924425cc0e7618d8bf76f96b45afa9ff19f924ed1ad57/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f2ff3baffc3a29c1f15bc9098aa0c09763410262d5e6cef42116f7356c184554", size = 1502943, upload-time = "2026-06-30T20:02:22.034Z" }, + { url = "https://files.pythonhosted.org/packages/80/dd/bbc1c38756350dddf7e24acae1c9482ef42051c267417e019aecc1ed4075/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0067b25fce104eaae5b88383de9ab803faeb671831e14ca698b771b356e2600f", size = 1497632, upload-time = "2026-06-30T20:02:23.517Z" }, + { url = "https://files.pythonhosted.org/packages/42/7e/9daffefcf5b97e6bb4c3e0b3c024c1aee9722f23d3cf7cd2ff80d6fb4a40/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c617417f9cbb0cb144f6283c3cbe0d2e0f01beaf9f608f662b21191058a626ec", size = 1448858, upload-time = "2026-06-30T20:02:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1f/f9baaab81a677ea0af7d2458cac2f94ebcc85958f8a3c15ba9d9e5dab653/ast_serialize-0.6.0-cp314-cp314t-win32.whl", hash = "sha256:5337cb256dcea3df9288205213d1601581536526b8f4da44b6974f1180f3252a", size = 1052600, upload-time = "2026-06-30T20:02:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1f/41b535866519512d8cf6669cb2cff7823b7672bb6279c0333b4ff89d7d9f/ast_serialize-0.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d947e45cafc4b09bd7528917fa84c517654a43de173c79785574b7b3068ac24", size = 1095570, upload-time = "2026-06-30T20:02:27.639Z" }, + { url = "https://files.pythonhosted.org/packages/50/64/e472fe3e3a2d33d874b987e8518aedf24562919e3b6161a4fa1797e89c0f/ast_serialize-0.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6e15ec740436e1a0d62de848641abe5f3a2f89a7f94907d534795ac91bbacf14", size = 1067267, upload-time = "2026-06-30T20:02:28.949Z" }, + { url = "https://files.pythonhosted.org/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367, upload-time = "2026-06-30T20:02:30.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657, upload-time = "2026-06-30T20:02:31.964Z" }, + { url = "https://files.pythonhosted.org/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620, upload-time = "2026-06-30T20:02:33.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075, upload-time = "2026-06-30T20:02:34.936Z" }, + { url = "https://files.pythonhosted.org/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348, upload-time = "2026-06-30T20:02:36.245Z" }, + { url = "https://files.pythonhosted.org/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580, upload-time = "2026-06-30T20:02:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693, upload-time = "2026-06-30T20:02:39.123Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517, upload-time = "2026-06-30T20:02:40.511Z" }, + { url = "https://files.pythonhosted.org/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843, upload-time = "2026-06-30T20:02:41.961Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698, upload-time = "2026-06-30T20:02:44.179Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209, upload-time = "2026-06-30T20:02:45.584Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464, upload-time = "2026-06-30T20:02:46.942Z" }, + { url = "https://files.pythonhosted.org/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164, upload-time = "2026-06-30T20:02:48.294Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014, upload-time = "2026-06-30T20:02:49.742Z" }, + { url = "https://files.pythonhosted.org/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165, upload-time = "2026-06-30T20:02:50.98Z" }, + { url = "https://files.pythonhosted.org/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444, upload-time = "2026-06-30T20:02:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, +] + +[[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 = "black" +version = "26.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/84/b3f55026206a9e8820a91503308075ca48eadc515e436731ca01dbe043b3/black-26.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9942db8888e06943c5dde66ca0037dcff82a2a4ec1ad0ada9e0d2ee9d9823893", size = 1987719, upload-time = "2026-05-18T17:05:02.757Z" }, + { url = "https://files.pythonhosted.org/packages/c6/34/7db312c5e5783d6e76cffd9d5ac8972a32badae4c6e3288dac0eed8d3bed/black-26.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:89c93167a74d3a75dfaa38a5c7cca015537d5820dd7f17d63267d674a61cae90", size = 1810083, upload-time = "2026-05-18T17:05:04.302Z" }, + { url = "https://files.pythonhosted.org/packages/33/e2/e0101e73c2c8727634e2efcb35e2b34bd23ad70dfa673789f5773a591b21/black-26.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f2cd76d069cc54c71f10360744ba8983fbb616903b4304a85b734915c8e1b4", size = 1860633, upload-time = "2026-05-18T17:05:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4c/e15c0c5b23cf3651035fe5addcce90e283af3548a3f91bb03d81b83106ab/black-26.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:87ed5c6f450580a2f6790bc7cbfb016dfc73bc750249762268a3695361315eef", size = 1477886, upload-time = "2026-05-18T17:05:07.96Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3f/59d43ade98d2ce5c8dc34a4e46cbecd177e6d55d7d4092969c6003ccc655/black-26.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:58b4bd92cf88aacf83d88479c8f9caee044b1ec55f2451a337354a7ea2590a22", size = 1277111, upload-time = "2026-05-18T17:05:09.473Z" }, + { url = "https://files.pythonhosted.org/packages/4b/96/3c3e09f09f44a37aac36b178a279cd19aa7001bd796187a7b162a294c81f/black-26.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96ae2c733b2aabdd9986e2c5df628ff3473676cd1c5faded1ff496cf6d74083c", size = 1970639, upload-time = "2026-05-18T17:05:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/5ad117b9ee3ecd933c712bcbae610006e5b7cc9f41c526cd7ed3b6c4124c/black-26.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e48b87e03bf109288e55cfceadcfa15ff5470aca2851a851950ed2926f450d7", size = 1792130, upload-time = "2026-05-18T17:05:12.983Z" }, + { url = "https://files.pythonhosted.org/packages/06/3a/7c448bc623fcdfa96672531beb5a616ea5e64f6975955254d7731ffb0ad9/black-26.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5119fa92ae61f786e8c3662fd60aece1d0a2dd5cca5d0c79417a95e7a4272a59", size = 1846134, upload-time = "2026-05-18T17:05:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5b/0b39b3a5917f0657ac014ad2edb58c139553a478adfe7f817abf1622ff6e/black-26.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:30d3c14661f2792e9142cce3eeeb1cbc175b3eb5f733be0c8eeb99651e52b0c3", size = 1478883, upload-time = "2026-05-18T17:05:16.542Z" }, + { url = "https://files.pythonhosted.org/packages/4c/48/dc222692e0f95030db1bbfb6c857e76858bad09058221ea7aae815255327/black-26.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:1ef92b76f7733f282fd096ea406200b5a286c42947412b0eaff3a74e3616cefe", size = 1277776, upload-time = "2026-05-18T17:05:18.029Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[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 = "coverage" +version = "7.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/8b/adeb62ea8951f13c4c7fef2e7a85e1a06b499c8d8237ea589d496029e53f/coverage-7.15.0.tar.gz", hash = "sha256:9ac3fe7a1435986463eaa8ee253ae2f2a268709ba4ae5c7dd1f52a05391ad78f", size = 925362, upload-time = "2026-07-02T13:10:50.535Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/97/c52dc440c390b6cfa87be9432b141a956e2d56d9b9f5fc8bd71c5f471722/coverage-7.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50913d4bf5ddafa6ca3693da5e4dd833dd1b772e0283c99ca7f7d287db67331a", size = 220539, upload-time = "2026-07-02T13:08:19.252Z" }, + { url = "https://files.pythonhosted.org/packages/3f/26/602de8c2aec7e2e3e99ebfb8e04ba65598f746275396eea5f6794ff4673f/coverage-7.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:359e141ccd33893ce3f1ad5525f8b96083003677c82182e5907d62d4ea5799fc", size = 221058, upload-time = "2026-07-02T13:08:21.013Z" }, + { url = "https://files.pythonhosted.org/packages/fc/13/ebab0743138891c1d646d61e247ec29639afcbb6c4e1905e6a0f0c75291a/coverage-7.15.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3200b6204935f928c64b2ca1f923ab8c1acb7c9de45ec61569711b34d25cccaf", size = 247797, upload-time = "2026-07-02T13:08:22.474Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b7/b6ffb9e042aa48dc4144a8a65529affaec8dca0685309353614a2a7386ad/coverage-7.15.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:be616bf61346883b2cfdc5178669647e03531d81ab761a7e378558b7e8bcb628", size = 249626, upload-time = "2026-07-02T13:08:23.803Z" }, + { url = "https://files.pythonhosted.org/packages/9c/06/243ff05b652333d8e3d060c11223efc2723b19cacf6605e433fa686ab5d4/coverage-7.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc7bafc3fe1059463a8fdd97ca79972d6e2bf819d775c7d54991b5b1971201d6", size = 251493, upload-time = "2026-07-02T13:08:25.397Z" }, + { url = "https://files.pythonhosted.org/packages/d3/2b/867faa17030a806114dae388b32a3fa929d8cd4bf39226fbc11f6e6bb705/coverage-7.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b713aa7fcf325a01d4184d848acb46fd84f78fdb0978470c636b23a06a753d91", size = 253406, upload-time = "2026-07-02T13:08:26.842Z" }, + { url = "https://files.pythonhosted.org/packages/94/c0/d789ce18f6605afc4895db75723424be2ef494282f77f61d8e5832923183/coverage-7.15.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e38e6fba2d56652fdfaf0231f8f78aeb805234a912de25dc291ee5cce5b8faa4", size = 248512, upload-time = "2026-07-02T13:08:28.398Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b6/b2673c30739f4a2e06649a0a38ad8b093c4d865462dc7bab0e9524a2c3b1/coverage-7.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:884499f42e382675be80770391983b90e0c0c774d87dbeeebf5f991cf6612b20", size = 249532, upload-time = "2026-07-02T13:08:29.731Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/acd79e9a41beabee92b623afe4f30b549916f48566271475f2907e752828/coverage-7.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:840481b12e083dbcbafab14794a8781a958edf327c8d3d70b4eee42f9b8253aa", size = 247537, upload-time = "2026-07-02T13:08:31.173Z" }, + { url = "https://files.pythonhosted.org/packages/12/d4/2d301c4d1b3238d7c88b70ab9d13fd53ed9505662a7ff1b46ba1e2e4e3c3/coverage-7.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:276646e9481703d09f854f3b2f018f24e19fd7049ae670a92570043eb97203b1", size = 251348, upload-time = "2026-07-02T13:08:32.63Z" }, + { url = "https://files.pythonhosted.org/packages/35/bb/c67708b2bc00f32e12805ec23d5fa677a0a51652f449341a89f9d6b1b715/coverage-7.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4de4b4d3f5545aa6c60dc4efd9c63b5b5dcc3bf00fe83146b2bdfffb8f6613bd", size = 247806, upload-time = "2026-07-02T13:08:33.931Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6c/57c4f653c47a6e917748f8938e389e72fbcae44e3643cd906664f0477a13/coverage-7.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5c504097b2a89b1e85bc6070d920df77daec701337e3aeef2c17775a5dd0ca90", size = 248410, upload-time = "2026-07-02T13:08:35.189Z" }, + { url = "https://files.pythonhosted.org/packages/6c/94/bb083041aef828903668f134273f319f2bd49224962875359c52faa5497f/coverage-7.15.0-cp310-cp310-win32.whl", hash = "sha256:f6e80ed91f98316e86b9c137206b04b2bcfbffccbdff49bd2eb09dddb1cf14e0", size = 222588, upload-time = "2026-07-02T13:08:36.486Z" }, + { url = "https://files.pythonhosted.org/packages/ef/94/a09d8ee618956f626741b0734854bac4425a00e10c0565f5abca64e7e751/coverage-7.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:b3b3e22030f3f6f5e01a5ce69936552a5c0f6992b7698777377b99041961031f", size = 223214, upload-time = "2026-07-02T13:08:37.885Z" }, + { url = "https://files.pythonhosted.org/packages/ae/23/82e910835ef4b8391047025e1d53aa48d66029f444eb8b25373c849bf503/coverage-7.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:003fff99412ea848c0aaebcc78ed2b6ce7d8a1227ed17e68470672770b78a02a", size = 220662, upload-time = "2026-07-02T13:08:39.205Z" }, + { url = "https://files.pythonhosted.org/packages/6d/0d/c7b213dde2f1579de5231062b386d8413f79c11667eb58c39319b25991da/coverage-7.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cbd804bf2784ce7b45114516050f346ecd50f960c4bb630a7ee9e1d78fa2118", size = 221168, upload-time = "2026-07-02T13:08:40.471Z" }, + { url = "https://files.pythonhosted.org/packages/33/77/d000aeedfac085088337b3c7becdad328474b1f8a9e4c9368a0c99605d68/coverage-7.15.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8773e15c23305b58882a4611fb9b2755977eae0dc2e515366a1b6c98866cc4c2", size = 251587, upload-time = "2026-07-02T13:08:42.033Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e0/86787c56b9df17afd370d5e293515dd4d9a107a561d13054873eefad8ecc/coverage-7.15.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f50e40081494c1dc4239ebb202014cbcc3306ea96fb6302a34c8cc0967fc5ae8", size = 253497, upload-time = "2026-07-02T13:08:43.387Z" }, + { url = "https://files.pythonhosted.org/packages/3f/02/181bc917359299c07dead6270f94e411151c8b60cec905c33499da69afe6/coverage-7.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daf96f37f5fc3a7b6c6da862eb4aee61c426bd63da236ed4a73ef0e503b4bca5", size = 255607, upload-time = "2026-07-02T13:08:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/b9/35/ca5e7427699913da6788c4f910e73ab16c5f4b59ec5d3a999dce2a45112f/coverage-7.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:51aa20f6ae2788fd197747766edf4cd8234fd9423309b934257fa6b21a592723", size = 257563, upload-time = "2026-07-02T13:08:46.334Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4d/b8220bacc2fc3c4e9078e27c32e99fb411479a4718a72bdd00036a9891c8/coverage-7.15.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03d1f922757662eb7af586e77834792274cff776bc7b1d1a0b66a49ea9d84735", size = 251726, upload-time = "2026-07-02T13:08:47.941Z" }, + { url = "https://files.pythonhosted.org/packages/c4/e4/2e145da1991d72189b9c3cf7eca05c716ee7080d099aaea6757cfc7df008/coverage-7.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a6d6acc9a7666245e6133dd15144ca038a85a9cd5026bb06d6bbae9e77440dc9", size = 253301, upload-time = "2026-07-02T13:08:49.5Z" }, + { url = "https://files.pythonhosted.org/packages/72/28/d2c841d698bf762e481f08bd4839d370246b6d9b61dab085a7b20b201a08/coverage-7.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ac2c4c27c7df851dc9a017c2d7de00b69147e84ba3d96f37a530b0b6fb51035", size = 251361, upload-time = "2026-07-02T13:08:51.304Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ed/55d9ffde994fba3897c0c783f77a7d053b0c18787f6892ed5b0aed73f469/coverage-7.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b761a1d504fd4bd1f20f418753964dca9f5862a511fc854dac58296b3b223671", size = 255129, upload-time = "2026-07-02T13:08:52.661Z" }, + { url = "https://files.pythonhosted.org/packages/1d/c0/ecbf33b8c460ea2718aeb813e2df8140d0370e5f67261c31524ceb0a2a8d/coverage-7.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e43b045e11c16e897895758ae90e4a90cf99e93d58549e2f90c0e2272e155695", size = 251081, upload-time = "2026-07-02T13:08:54.188Z" }, + { url = "https://files.pythonhosted.org/packages/a9/de/fb87b4261f54448dd2b9504ef19a58be42cef0d9520595fbfe1219b15234/coverage-7.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:589b54513e901739f4b4582c705ce96b80c96f57641b1464607e2367a270e540", size = 251988, upload-time = "2026-07-02T13:08:55.726Z" }, + { url = "https://files.pythonhosted.org/packages/df/27/3494d5f291b9a4cb868f73c11221a8bd2d5bd761a8f9acea61ff57128dd1/coverage-7.15.0-cp311-cp311-win32.whl", hash = "sha256:106781b8482749162d0b47056937ba0933508e5d9447f65a5e7d5c422f0d6bb4", size = 222754, upload-time = "2026-07-02T13:08:57.091Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ee/cd4847ebc9be6a9c0123d763645a6f1f3be6b8c58c962706368b79cbac07/coverage-7.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:821e92b3631d762a339695824cadbbc73020354eba2a23a551a99ad34938fbe6", size = 223225, upload-time = "2026-07-02T13:08:58.594Z" }, + { url = "https://files.pythonhosted.org/packages/57/37/5011581aa7f2be498b97dcc7c9902192442a42f4f9a748aeadb3d6506b42/coverage-7.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:309990eb5fb8014b9f67cb211f7fd41876ec8a88a88d3ae76de0ed1d611e3640", size = 222774, upload-time = "2026-07-02T13:09:00.074Z" }, + { url = "https://files.pythonhosted.org/packages/2a/74/fd4c0901137c4f8d81a76ada99e43c65163b4c94a02ece107a4ec0c6b615/coverage-7.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b75ee5e8cb7575636ac598719b4307ac529ec8fcd79608a35c3cd4d4dada812d", size = 220838, upload-time = "2026-07-02T13:09:02.084Z" }, + { url = "https://files.pythonhosted.org/packages/0f/2e/2347583467bd7f0402635101a916961915cc68fce652cd0db5f173ea04fc/coverage-7.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffb31267816b93b075302248cc1737506081b4f163df4401e9df1a6424aafabe", size = 221197, upload-time = "2026-07-02T13:09:03.617Z" }, + { url = "https://files.pythonhosted.org/packages/f0/17/99fa688541ae1d6e84543a0e544f83de0c944815b63e9e7b1ed411d15036/coverage-7.15.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e4d0bb73455bf97ab243a8f12c37c686ccf1c13bb614b7b85f1d062f06f42b2c", size = 252705, upload-time = "2026-07-02T13:09:05.059Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/6a95a5cd83b74839017ef9cf48d2d8c9ae60af919e17a3f336e6f9f1b7bd/coverage-7.15.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:20d9ccc4ebd0edc434d86dfd2a1dd2a8efa6b6b3073d0485a394fee86459ebb4", size = 255441, upload-time = "2026-07-02T13:09:06.559Z" }, + { url = "https://files.pythonhosted.org/packages/67/f2/406f6c57d600f68185942422c4c00f1a3255d60aee6e5fd961425cd9987e/coverage-7.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20c8a976c365c8cb12f0cbd099508772ea41fb5fa80657a8506df0e11bd278c5", size = 256556, upload-time = "2026-07-02T13:09:08.197Z" }, + { url = "https://files.pythonhosted.org/packages/74/8e/d3fa48489c15ecdec1ba48fd61f68798555dddd2f6716f9ad42adeb1a2a9/coverage-7.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f948fd5ba1b9cbca91f0ae08b4c1ce2b139509149a435e2585d056d57d70bf01", size = 258815, upload-time = "2026-07-02T13:09:09.691Z" }, + { url = "https://files.pythonhosted.org/packages/47/2e/2d40ddd110462c6a2769677cf7f1c119a52b45f568978fc6c98e4cc0dd0f/coverage-7.15.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f58185f06edf6ad68ec9fb155d63ef650c82f3fbd7e1770e2867751fb13158f4", size = 253117, upload-time = "2026-07-02T13:09:11.212Z" }, + { url = "https://files.pythonhosted.org/packages/51/c0/310782f0d7c3cb2b5ac05ba8d205fe91f24a36f6bf3256098f1782181c38/coverage-7.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02adc79a920c73c647c5d117f55747df7f2de94571884758ce8bc58e04f0a796", size = 254475, upload-time = "2026-07-02T13:09:13.029Z" }, + { url = "https://files.pythonhosted.org/packages/86/f7/702da6c275f8ae6ade423d2877243122932c9b27f5403003b9ef8c927d12/coverage-7.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6eb7c300fbed667fd6e3588eba71c1904cdb06110ca6fdf908c26bdd88b8e382", size = 252619, upload-time = "2026-07-02T13:09:14.699Z" }, + { url = "https://files.pythonhosted.org/packages/fb/84/c5b15a7e5ecba4e56218d772d99fe80a63e63f8d11f12783723a6005ab45/coverage-7.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b5fb23fa2de9dce1f5c36c09066d8fcda16cd96e8e26686caa2d7cb9b567d65c", size = 256689, upload-time = "2026-07-02T13:09:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/95/2f/c8b07559b57701230c61b23a953858c052890c12ef568d81780c6c46e92e/coverage-7.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cec79341dbe6281484024979976d0c7f22beae08b4a254655decd25d42cbe766", size = 252189, upload-time = "2026-07-02T13:09:17.828Z" }, + { url = "https://files.pythonhosted.org/packages/6b/80/6d2f049dd3fd3dbfd60b62ba6b2162a04009e2c002ce70b24cf3878dec7a/coverage-7.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c664c5444b1d970b1b2a450e21fb19ee5c9cfdf151ded2dda37260031cca0da", size = 254059, upload-time = "2026-07-02T13:09:19.304Z" }, + { url = "https://files.pythonhosted.org/packages/ce/92/b0287a2c42031d25c628f815f89a3cd9f8268ee78bb1252c9356cda1c689/coverage-7.15.0-cp312-cp312-win32.whl", hash = "sha256:5f764a3fa339bde6b3aa97657f5a6a3a9451e4a5b4ea98a2892c773a43525f77", size = 222893, upload-time = "2026-07-02T13:09:20.812Z" }, + { url = "https://files.pythonhosted.org/packages/a9/69/e34c481915fecb499b3146975061dac528752e37706edc1804f32c822469/coverage-7.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:52f9a4d2c4c56c8848bc2f524916698354b0211488b38c49ad9ae54f6cafbff6", size = 223429, upload-time = "2026-07-02T13:09:22.315Z" }, + { url = "https://files.pythonhosted.org/packages/fe/98/6e878f0b571d32684ef3f38d7c03db241ca5b82a5da8a5391596a8f209c4/coverage-7.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:31e5c3e70c85307ea35a12964e2e40f56ca2ee4b1c8c721ccf4609d17071080b", size = 222810, upload-time = "2026-07-02T13:09:23.812Z" }, + { url = "https://files.pythonhosted.org/packages/76/04/145a3748098bcc86b631a85408d2c3dc5c104e0bd86d605468239b25b6c4/coverage-7.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5be4caf3b28836f078abe700f8944dac4a65d78f16d6c600c89cb624e5535782", size = 220863, upload-time = "2026-07-02T13:09:25.371Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5c/4ed55708fed2c64b63c9bc5715daef670872202101938869b7fe5d5fbb8f/coverage-7.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dd58ad1404704303ca8d4f4b8a1095e7cbc7040ef17a66df1e6619aa10176430", size = 221230, upload-time = "2026-07-02T13:09:26.897Z" }, + { url = "https://files.pythonhosted.org/packages/7b/19/3a80b97d3b2a5c77a01ae359c6bed20c13738fe3d9380f08616d4fec0281/coverage-7.15.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bbcbb317c2e5ded5b21104af81c29f391be2af98d065693ffbe8d23949b948e5", size = 252227, upload-time = "2026-07-02T13:09:28.543Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/b70062750686bd7da454da27927622f48bbac6990ac7a4c4a4653e7b0036/coverage-7.15.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:27f31ecb458da3f859aab3f15ada871eb7a7768807d88df4a9f186bb17737970", size = 254823, upload-time = "2026-07-02T13:09:30.177Z" }, + { url = "https://files.pythonhosted.org/packages/a9/09/dad6a75a2e561b9dc5086a8c5257a7591d584246f67e23e70d2995b89ab6/coverage-7.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fb759be317fdc62e0f56bffdf61cfcb45c7761ad6b71e3e583e71a67ae753c", size = 256059, upload-time = "2026-07-02T13:09:31.979Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e7/b5d2941fa9564573d44b693a871ff3156f0c42cbefe977a09fa7fdc59971/coverage-7.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5cf007add5ab4bb8fa9f4c77e3732127c9e6cad501d7db43355fbfafca0be84", size = 258190, upload-time = "2026-07-02T13:09:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/7c/1d/8e895bcde3c57ccd46d896dda5f2b3d5df761a1b0c6c9d450d175dedc632/coverage-7.15.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc78d9843bd576fbe2118248258d485e968dc535f95ed504a7b0867ba9b51389", size = 252456, upload-time = "2026-07-02T13:09:35.765Z" }, + { url = "https://files.pythonhosted.org/packages/14/4c/f6997da343ddeb959be82c3b05322793f92c071ad45f7cb8a96336e2dd5f/coverage-7.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a263060f1de0b4b74b4e089c2a70b8003b3781c733329a9c8fd54995328f9950", size = 254192, upload-time = "2026-07-02T13:09:37.445Z" }, + { url = "https://files.pythonhosted.org/packages/17/27/a0bc09d032267b9da89d95a2d874cfbef2a5aebbf0e87cf7aba221d79a99/coverage-7.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c48decf16e0dfd5b049c7d5e82200c23c08126719142998d4f172444e3d0529e", size = 252153, upload-time = "2026-07-02T13:09:39.422Z" }, + { url = "https://files.pythonhosted.org/packages/54/c0/77fc233d9fba07b244c40948c53fe27308b8f21732fb3417f87fbd6fd992/coverage-7.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:08fb028000ed0aaa0a4cbdfbb98be7cb42f370db973fbbb469733505ab20e13e", size = 256310, upload-time = "2026-07-02T13:09:41.006Z" }, + { url = "https://files.pythonhosted.org/packages/d5/24/601cecfb5825becacb8d45219a018a3b55b9dbaec624efdb0ea249d08be2/coverage-7.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb7dc0c3b7d8a1077abea0b8546ebc5e26d6ef6ecefc2f0f5ad2b8a53bdad837", size = 251974, upload-time = "2026-07-02T13:09:42.733Z" }, + { url = "https://files.pythonhosted.org/packages/47/1e/6f45e5a5b3d5484318d368702af6716b5ab8913b0428bec981a562fcf296/coverage-7.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cb3602054ccbe9f0d8c2dc04bbeba90d5719236e2cd06e042ddd6d3fc7b6e37", size = 253745, upload-time = "2026-07-02T13:09:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4df027a77bd11d0e527f44c53557c76e54ad027413d0304252ea3a78d67e/coverage-7.15.0-cp313-cp313-win32.whl", hash = "sha256:0bf781da64326b677be344df505171435b6f58716108606621d5d27d964fff8b", size = 222902, upload-time = "2026-07-02T13:09:46.122Z" }, + { url = "https://files.pythonhosted.org/packages/a0/10/0355894d34e231f2c5449e71287e81a50793a325df2e2b027b7bcd9dfd19/coverage-7.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:2c57a275078ee3fa185f83e400f765bc764a549de66d99b47881645cbd4ea629", size = 223444, upload-time = "2026-07-02T13:09:47.687Z" }, + { url = "https://files.pythonhosted.org/packages/06/ef/bb725f263befaaff851203ab338e68af15e195d7f7b5f323162532d9b6a8/coverage-7.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:3812c61afc6685c7999b39320779ab8f43b7a3081fdb0def39976e56fbdb9a21", size = 222839, upload-time = "2026-07-02T13:09:49.717Z" }, + { url = "https://files.pythonhosted.org/packages/4f/9c/1e3ca54f72a3185ece06c58d871099898c48f0ed6430d17b6ab75f0d180a/coverage-7.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:41cb79af843222e11da87127ad0ecbfa878abadd0f770a4a99391a27d3887324", size = 220906, upload-time = "2026-07-02T13:09:51.339Z" }, + { url = "https://files.pythonhosted.org/packages/09/37/f718613d83b274880382f6b67e78f3802549ae39b0b3e65ae5b5974df56e/coverage-7.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7d2008989ef8fe54188d3f3bfa2e3099b025af11e90a6a1b9e7dc433d04263d8", size = 221239, upload-time = "2026-07-02T13:09:53.138Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/22bae91e0b75445f68d365c7643ed0aa4880bbf77450ee74ca65bdae53a7/coverage-7.15.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:769e8ece11a596315ebf5aa7ec383aeeed016c091d2bf6363ffb996d41529092", size = 252286, upload-time = "2026-07-02T13:09:54.996Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/bec5e32aa508615d9d7a2790effb25fb4dc28606e995816afe400b25ece3/coverage-7.15.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:65a6b6164ee5c39e2f3803f314292d6c61a607ba7fee253d1e03c42dc3903502", size = 254789, upload-time = "2026-07-02T13:09:56.678Z" }, + { url = "https://files.pythonhosted.org/packages/17/29/0e865435b4354e4a7c03b1b7920046d31d0a273d55decefea27e011cb9bf/coverage-7.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75128817f95a5c45bb01d65fd2d8b9cb54bbe03d81608fb70e3e14b437ad56c2", size = 256135, upload-time = "2026-07-02T13:09:58.343Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/33a870b58a13325d62fc0a6c8f01fa0ff667cef60c7498e2382a147dfa18/coverage-7.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9887bb428fe2d4cd4bee89bac1a6c9932f484afd5b36fbd4ff6ea5f825bb1f5e", size = 258449, upload-time = "2026-07-02T13:10:00.057Z" }, + { url = "https://files.pythonhosted.org/packages/18/7b/6fffe596bf3ddba8462758d02c5dad730fd91055a6634aa2e4226229181a/coverage-7.15.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0bfc0be1f702042207a93a00523b1065ee1fe951e96edf311581c0bbc2e34888", size = 252313, upload-time = "2026-07-02T13:10:01.946Z" }, + { url = "https://files.pythonhosted.org/packages/58/1b/11468dd6c1676ab831a70cb9a8d4e198e8607fa0b7220ab918b73fe9bfbd/coverage-7.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f64627d55def5a43282d70e08396672692f77e4da610a5bb8bb4060b432b6859", size = 254142, upload-time = "2026-07-02T13:10:04.065Z" }, + { url = "https://files.pythonhosted.org/packages/79/41/29328e21d16b1b95092c30dd700e08cf915bd3734f836df8f3bdb0e8fa9f/coverage-7.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2c6f0fa473003905c6d5bac328ee4eba9fbea654f15bc24b8a3274b23363fa99", size = 252108, upload-time = "2026-07-02T13:10:06.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/de/05ccfb990439655b35afbfd8e0d13fe66677565a7d4eb38c3f5ef2635e1c/coverage-7.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2bcf9afaf064172c6ec3c58a325a9957ad1178c05dd934e25f253321776e0676", size = 256385, upload-time = "2026-07-02T13:10:08.141Z" }, + { url = "https://files.pythonhosted.org/packages/51/0e/486828a3d2695ea7a2609f17ff572f6b01905e608379440a11da4b8dffbe/coverage-7.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:baf06bc987115d6fb938d403f7eab684a057766c490367999a2b71a6883110c6", size = 251923, upload-time = "2026-07-02T13:10:10.179Z" }, + { url = "https://files.pythonhosted.org/packages/18/c7/03582b6715f078e5e558354c87616d945b9894cda2dace8e4009b17035e4/coverage-7.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f0405f2ff97b1c4c0e782cb32e02f32369bcf2e6b618b591d67e1ea754575dfe", size = 253580, upload-time = "2026-07-02T13:10:12.052Z" }, + { url = "https://files.pythonhosted.org/packages/db/dc/9e578bbaf2ecb4959a81b7e7601ad8cca772cba2892e8d144cb749b4a71a/coverage-7.15.0-cp314-cp314-win32.whl", hash = "sha256:ab282853ed5fbd64bbb162f19cb8fcb7087187508a6374b4f9c34ec1577c4e8f", size = 223107, upload-time = "2026-07-02T13:10:13.994Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3e/c8c3b75d8dbe0e35f7b0cc3ff5e949fc59500f70b21d0398813f66740664/coverage-7.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bb3040e9f4bbe26fcb0cd7cc85ac63e630d3f3a9c74f027abf4caa27e706663", size = 223597, upload-time = "2026-07-02T13:10:15.906Z" }, + { url = "https://files.pythonhosted.org/packages/cd/bc/3cbc9fb036eb388519bccd521f783499c39b64256013fbc362782f196fe1/coverage-7.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:346771144d34f7fa84ec28386f78e0f31653f33cf35e19d253d5b35f9e8201da", size = 223020, upload-time = "2026-07-02T13:10:17.844Z" }, + { url = "https://files.pythonhosted.org/packages/28/00/199c4a8d656dff63102577a056c0fce2ff6a79e40adac092fc986c49cbf1/coverage-7.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d34a010905fb6401324ba016b5da03d574967f7b21ce48ea41e66f0f1f95f641", size = 221638, upload-time = "2026-07-02T13:10:19.703Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8e/9d0092c96a3d3a26951ecc7020826aa57bcb1b119ca81acbba996884ab13/coverage-7.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bb25d825d885ca8036795dacfc3924d33091fc76d71ebc99420c6b79e77d96fa", size = 221903, upload-time = "2026-07-02T13:10:21.514Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b4/c0ca3028f42c9a08e51feb4561ef1192e5de99797cd1db5b04590c215bda/coverage-7.15.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:94c9686bfe8a9a6810297aecbd99beaa3445f9e8dc2f80b1382cca0d86b64461", size = 263267, upload-time = "2026-07-02T13:10:23.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/aa/a375e3846e5d3c013dc600b2a3231089055c73d77f5393dd2192a8d64da6/coverage-7.15.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9bd671c25f9d85f09d7ec481d0e43d5139f486c06a37139847a7ce569788af72", size = 265390, upload-time = "2026-07-02T13:10:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/92/e1/5783cdabb797305e1c9e4809fea496d31834c51fa772514f73dc148bcfc9/coverage-7.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:110cbdf8d2e216577312cf06ccf85539c0e5a5420ef747e4a4719b5e483c88cd", size = 267811, upload-time = "2026-07-02T13:10:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/85/31/96d8bbf58b8e9193bc8389574a91a0db48355ee98feb66aa6bf8d1b32eea/coverage-7.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2c5d4619214f1d9993e7b00a8600d14614b7e9d84e89507460b126aa5e6559e5", size = 268928, upload-time = "2026-07-02T13:10:29.242Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7a/5294567e811a1cb7eda93140c628fa050d66189da28da320f93d1d815c73/coverage-7.15.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:781a704516e2d8346fbbd5be6c6f3412dd824785146528b3a01816f26c081007", size = 262378, upload-time = "2026-07-02T13:10:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/69/3f/3f48538421f899f28946f90a3d272136a4686e1abf461cc9249a783ee0f3/coverage-7.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd4a1b44bcb65ee29e947ac92bbee04956df3a6bfc6143641bb6cae7ede00fc9", size = 265263, upload-time = "2026-07-02T13:10:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d3/092df15efcab8a9c1467ee960eb8019bbad3f9300d115d89ea6195f369ff/coverage-7.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0e4950c9d6d3e39c64c991814ff315e2d0b9cb8152363594212c9e55208c0a8f", size = 262866, upload-time = "2026-07-02T13:10:35.104Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ab/0254d2b88665efb2c57ad368cc77ab5de3435bd8d5add4729c1b0e79431e/coverage-7.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:fe9c87ff42e5472d80d21704972e1f96e104a0a599d77c5e35db5a3c562e2571", size = 266599, upload-time = "2026-07-02T13:10:37.05Z" }, + { url = "https://files.pythonhosted.org/packages/a8/79/1cfa4023e489ce6fbc7be4a5d442dbc375edb4f4fda39a352cedb53263c2/coverage-7.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f00d5ae1dd2fe13fb8186e3e7d37bcbd8b25c0d764ff7d1b32cef9be058510a8", size = 261714, upload-time = "2026-07-02T13:10:38.966Z" }, + { url = "https://files.pythonhosted.org/packages/b7/eb/fee5c8665656be63f497418d410484637c438172568688e8ac92e06574e7/coverage-7.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:363ab38cc78b615f11c9cac3cf1d7eef950c18b9fdedfb9066f59461dcf84d68", size = 264025, upload-time = "2026-07-02T13:10:40.789Z" }, + { url = "https://files.pythonhosted.org/packages/ab/99/63005db722f91edc81abc16302f9cc2f6228c1679e46e15be9ae144b14d0/coverage-7.15.0-cp314-cp314t-win32.whl", hash = "sha256:54fd9c53a5fafff509195f1b6a3f9be615d8e8362a3629ff1de23d270c03c86b", size = 223413, upload-time = "2026-07-02T13:10:42.597Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e8/2bc6181c4fb06f1a6b981eb85330cc57bfad7e3f710fc9c9d350013ba228/coverage-7.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:87b47553097ba185ed964866078e7e63adea9f5f51b5f39691c34f30afd21080", size = 224245, upload-time = "2026-07-02T13:10:44.47Z" }, + { url = "https://files.pythonhosted.org/packages/79/b8/4d959bf9cc45d0cfed2f4d35cafcab978cdb6ea02eb5100009cd740632a3/coverage-7.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aeefb2dd178fe7eee79f0ad25d75855cb35ee9ed472db2c5ea06f5b4fd00cec5", size = 223558, upload-time = "2026-07-02T13:10:46.368Z" }, + { url = "https://files.pythonhosted.org/packages/52/30/21b2ad45959cd50e909e02ebac1e30b4ceb7162e91c11d4c570223a458b7/coverage-7.15.0-py3-none-any.whl", hash = "sha256:56da6a4cbe8f7e9e80bd072ca9cefe67d7106a440a7ec06519ec6507ac94ad19", size = 212632, upload-time = "2026-07-02T13:10:48.641Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "datamodel-code-generator" +version = "0.68.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "black", marker = "sys_platform != 'emscripten'" }, + { name = "genson" }, + { name = "inflect" }, + { name = "isort", marker = "sys_platform != 'emscripten'" }, + { name = "jinja2" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/9d/9746c36d7b597235dae8806696fc158fa41a3f78912499f6cfebeb2b7833/datamodel_code_generator-0.68.1.tar.gz", hash = "sha256:3a9f0c501670b35d6ea1764b3ff6c552f1f594c7ee3ed9cab5b3b72f75ff092d", size = 1577260, upload-time = "2026-07-08T00:47:56.697Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ed/03aab13a7b425a816f61c580fc9fb7ad8902fb4839a205dfb2e69a43fdd7/datamodel_code_generator-0.68.1-py3-none-any.whl", hash = "sha256:2784ad944f25579be874d40e7f28ee5e7ab2d4457a08612393c7fd02ea7b7f21", size = 425059, upload-time = "2026-07-08T00:47:54.903Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "genson" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/53/de162dc8e03fccd9ebe59d17c7812378fe8bd2b604f6b1b94d00165140ac/genson-1.4.0.tar.gz", hash = "sha256:bc7f1c1bae87a21ca44d81149aec95a3f4468d676de9b8b08caa064f3c50b3da", size = 47908, upload-time = "2026-07-06T08:21:50.331Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/02/767f744ab6d4cb7761e5008acc3d534b7a0481af62563d52e391fbcb2140/genson-1.4.0-py3-none-any.whl", hash = "sha256:03bc71bbe52defde70660cc4dcd1ea1097997da5a1cbb90a9dbd3acc7c9e1b65", size = 24484, upload-time = "2026-07-06T08:21:49.046Z" }, +] + +[[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 = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "inflect" +version = "7.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, + { name = "typeguard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751, upload-time = "2024-12-28T17:11:18.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" }, +] + +[[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 = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + +[[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 = "librt" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/2f/3908645ddddab7120b46295e541ead308109fa48dbec7d67d7a778870d60/librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781", size = 211402, upload-time = "2026-07-08T12:26:29.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/2f/ec5241c38e7fa0fe6c26bfc450e78b9489a6c3c08b394b85d2c10e506975/librt-0.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34e47058fcc69a313293d6dee94216a4f30c929ae6f2476e58c5ba635aa639d5", size = 148654, upload-time = "2026-07-08T12:24:30.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1a/d651e18d3ee7aa2879322368c4f278bb7ecaa6b90caadfdec4ebfa8389f3/librt-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dbdd5b6509d0c2a8fe72cf494c299a61dbd58142a90a4190664ae159e4a7b547", size = 153537, upload-time = "2026-07-08T12:24:31.773Z" }, + { url = "https://files.pythonhosted.org/packages/45/18/10bff2122577246009d9619b6569596daf69b7648812f997ca9ca0426f60/librt-0.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e56ea4ee4df77585a6b5c138f6538680886024fa559f5b55bd14b12e98e67b2", size = 494336, upload-time = "2026-07-08T12:24:33.079Z" }, + { url = "https://files.pythonhosted.org/packages/67/69/87dfee871b852970f137fdeae8e2ca356c5ab38e6f21d2a3299535fc3159/librt-0.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f1f9cc4d09a46d9cb3c2063ae100629d3f52a6517c3c08c2f4c9828261883929", size = 485393, upload-time = "2026-07-08T12:24:34.324Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d5/625447a8c0441ff5f15f4ac5e1d323fb9d4d256ebfde7a3c8e003f646057/librt-0.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f125f5d46b20f89dc5587a55cc416b4ba2a5b2ffda36d048ee120e17598a653a", size = 515382, upload-time = "2026-07-08T12:24:35.575Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d8/1c8c49ea04235960426444deece9092a6b3a9587a850a81bae2335317411/librt-0.13.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2608d3b39f9e0b4a66a130d9150c615cba40a5090d25eeeaa225e0e46de8c0ac", size = 509483, upload-time = "2026-07-08T12:24:36.923Z" }, + { url = "https://files.pythonhosted.org/packages/6f/65/f1760fc48050e215201a03506c32b7270159088d01f64557b53e39e74a45/librt-0.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fd35e95ab5e45c3901d37110263c7db85a961110f5460588fe37f8c131f88a7", size = 532503, upload-time = "2026-07-08T12:24:38.203Z" }, + { url = "https://files.pythonhosted.org/packages/18/1b/793e281dcf494879eff99f642b63ebc9c7c58694a1c2d1e93362a22c7041/librt-0.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5f31b0aa13c9b04370d4da6be1ab7779776b3a075cceb6747a39a4be85fe1e40", size = 537027, upload-time = "2026-07-08T12:24:39.34Z" }, + { url = "https://files.pythonhosted.org/packages/69/45/0801bbb40c9eea795d3dd3ce91c4c5f3fe7d42d23ec4be3e8cb283bcc754/librt-0.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0b795f5fc70fbbb787ceaf79bb3a0d627bcc33c53de51741755263ec406b775a", size = 517100, upload-time = "2026-07-08T12:24:40.907Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6c/eb5f514f8e29d4924bc0ff4601dd7b4175557e182e7c0721e84cffa39b8a/librt-0.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36b306a623aaad96fe4b378692b54f9c0789fccd833b9851753d5fbf6138cfde", size = 558653, upload-time = "2026-07-08T12:24:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bf/f140100d1b59fe87ff40b5ecbb4e27924335b189a784e230ee465452f6c2/librt-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a3762e75fcac8c9e4dacaaf438bffd9003e2ca2c531b756f3c0035deefa674c8", size = 104402, upload-time = "2026-07-08T12:24:43.668Z" }, + { url = "https://files.pythonhosted.org/packages/22/7c/57e40fef7cfb61869341cb28bdcefe8a950bebcbecca74a397bae14dce4a/librt-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:d63bae12a8aeb51380be3438e4dc4bd27354d0f8e19166b2f44e3e94d6f552dc", size = 125002, upload-time = "2026-07-08T12:24:44.793Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/a6498964cfeec270c468cffdc118f69c29b412593610d55fa1327ca51ff4/librt-0.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b5a7bbff495baedbd9b916c367d66854008f8f3b575908ded477c499dc60082", size = 148029, upload-time = "2026-07-08T12:24:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/dc86d1bffd8e0c2818bace29d9f7783cfbb8e0673bf3673b5bbd5bbe0420/librt-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34bc7938b9fdf14fe32a406c19c71faf894c5cee7e7474bd0be2f17200b82d14", size = 153036, upload-time = "2026-07-08T12:24:47.257Z" }, + { url = "https://files.pythonhosted.org/packages/29/3f/b923826660f02f286186cd9303d52bb05ced0a13708edc104dc8480920e3/librt-0.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f40e56b61b41be5f7dec938cfeffd660668cf4b5e72c78e7bd671d66b7bc2c79", size = 493062, upload-time = "2026-07-08T12:24:48.483Z" }, + { url = "https://files.pythonhosted.org/packages/88/87/6c0980a9c9b1302cb68d108906697b89eceb55889bb1dcf77c109aa56ca5/librt-0.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:9c5d02b89de5acd0379a51ec44a89476fb03df6145442e1c8ecd6bee2f91b176", size = 485510, upload-time = "2026-07-08T12:24:49.727Z" }, + { url = "https://files.pythonhosted.org/packages/32/81/795ae3b9df5dd94079fb807e38191855e023e8c6249014ae6bc3f0d9a490/librt-0.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7db9a3ff32ef5f7d1703d93831a3316cdf0b537de6a1cc03cc8fdd09b9194e89", size = 515909, upload-time = "2026-07-08T12:24:51.135Z" }, + { url = "https://files.pythonhosted.org/packages/20/e5/182de15abce8907108a6fdb41487de65beb5099b74dc5841b19b099168db/librt-0.13.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3dbb2a31882456cadc7053378e81ad7ed7693db4ac9f98ab5f81ef034aa8ec9f", size = 508620, upload-time = "2026-07-08T12:24:52.358Z" }, + { url = "https://files.pythonhosted.org/packages/32/03/33978d32db76e1f66377e8f78e42a2ca3c162143331677d1f50bbad36cfb/librt-0.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c6014e3c80f9c1fe268ef8b0e0ef113bac672cc032f2f93866e7ddad4f3e663d", size = 530363, upload-time = "2026-07-08T12:24:53.503Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f5/b291fbd2d00f7d8287bcbf67b5aa0c6afed4bc26cef23e079629c47a2c04/librt-0.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:091b60a4d2174fc1ec5c34cdc0b72efb6224753d76b7da61ebeab7a191aec8bd", size = 534209, upload-time = "2026-07-08T12:24:55.138Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/6f41f17939d191bc21609f220da8509316bc62797f078545fe83be522e78/librt-0.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:66cb1138f384a191a6d75f986064841fcfdc0cea98f7bd9c9ab9b38049917588", size = 514254, upload-time = "2026-07-08T12:24:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/af/c2/2e4befa5410a7443019c14abccc94ff619797171f6b72013635fb87f31d7/librt-0.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:17221a7569f8f292aa0014226e48aa25b8c2b08da18088cd230953d0ea0f9cd1", size = 557611, upload-time = "2026-07-08T12:24:57.561Z" }, + { url = "https://files.pythonhosted.org/packages/ab/54/8b69f81448417adbc040a2185f4e2eece1e1994b7dcfaeed4662b30f98a5/librt-0.13.0-cp311-cp311-win32.whl", hash = "sha256:fc67741da44c6eaa90e01eafb586bbba9b51eb5b6ed381ee6f5ae72eb3316d21", size = 104906, upload-time = "2026-07-08T12:24:58.806Z" }, + { url = "https://files.pythonhosted.org/packages/76/5a/f4aaf37b50f2fde12c8c663b83fdd499cdc24f957f19543d7414bfcc9e25/librt-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc99dfb62b23c9207c33d0be8a2e2af7a42e21e6ea388b380a0c948c7b88953b", size = 125852, upload-time = "2026-07-08T12:25:00.065Z" }, + { url = "https://files.pythonhosted.org/packages/f2/99/bf1820e6feeabc2f218c24450ec0c995d6a91e8ba0fd3caf042c9e8adb2a/librt-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:40ccd13c252d3fe473ffc8a57be7565abc8b64cf1b108344c859d5164f7f3e0c", size = 111832, upload-time = "2026-07-08T12:25:01.148Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/b2933ddae222dac338476abb872641169a5cfed2c2bb5444a5b07b32b0c3/librt-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30536798f4504c0fad0885b1d371b0539abb081e4570c9d7c641cb51141b49f0", size = 150990, upload-time = "2026-07-08T12:25:02.42Z" }, + { url = "https://files.pythonhosted.org/packages/90/ef/db98f744ca50e6efc9c95c70ee49b77aefac31f6a3fc7c83754a42d6a74f/librt-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93d24ebb82aa4420b1409c389e7857bc35bd0b668007ac8172427d5c73cc8cc5", size = 155238, upload-time = "2026-07-08T12:25:03.681Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/a197e7bc72baf2c61ce7fdc6906a5054dc05bd8da0819aa894e4857bf87e/librt-0.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb8a1adce42d8b75485a5d56a9623a50bcab995b6079f1dac59fc44034dd93d9", size = 503073, upload-time = "2026-07-08T12:25:05.049Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e7/7887712e27da7c1ab80fcabb1de6eb24243964f6557cae530d4b70706dbd/librt-0.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0763ca2ab66058174f9dee426dc64f5e0a89c24a7df8d3fe3f1836c04e25de4b", size = 496528, upload-time = "2026-07-08T12:25:06.26Z" }, + { url = "https://files.pythonhosted.org/packages/94/f0/f2283385bb6b950b26a1410f4ce51ec27231e0b3a4b925c46366d218b198/librt-0.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b222493da6e7b6199db9bd79502436cf5a27da3c1f7fa83c7e285444fc93fd03", size = 531786, upload-time = "2026-07-08T12:25:07.658Z" }, + { url = "https://files.pythonhosted.org/packages/36/11/69ac3b54766ffba5fd7e5acebfb048d66dbe1f9f2d14516c2b3edc59cf87/librt-0.13.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fadc63331f4388c3dc90090448f682a7e9feafc11481391c1e94f2f907a3976e", size = 524393, upload-time = "2026-07-08T12:25:09.121Z" }, + { url = "https://files.pythonhosted.org/packages/61/5f/d72f95fd444a926a3c14b4e24979474116988dd57a45be242077c45d3c22/librt-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70d9c62a4cffd9f23396cd5ef93fc5d11b31596b9b7d6306074abe3d5fcf09bd", size = 543026, upload-time = "2026-07-08T12:25:10.459Z" }, + { url = "https://files.pythonhosted.org/packages/c4/08/dcd9993ad192737a004ba263d549f8ea605b326b952e7d6205c7d4170b76/librt-0.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66c0e7e6b02a155576df2c77ec933a70b72da726e248c494abf690923e624348", size = 546829, upload-time = "2026-07-08T12:25:11.716Z" }, + { url = "https://files.pythonhosted.org/packages/96/d5/6d9bb2f54e4109a956b7128836529653eb9d740f784bc47ed10a02c1000e/librt-0.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ac04bcd3328eb91d99dfedf6a60d9c1f15d3434e6f6daf922f0420f7d90b85c7", size = 535700, upload-time = "2026-07-08T12:25:13.144Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f2/10946922503858a359492fa27f13e86228bde702116a740ac7b3cd185f24/librt-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db327e7271e653c32040b85ae6188059c924b57d7e1e29f935523fa017cd4e82", size = 573566, upload-time = "2026-07-08T12:25:14.336Z" }, + { url = "https://files.pythonhosted.org/packages/48/a8/94f00e3c99479a18088af3685ea016c42f3c7d5d1964d8dbb40c08d7f1aa/librt-0.13.0-cp312-cp312-win32.whl", hash = "sha256:860bd1d8ba48456ce08feaf8d343a8aaeb2fa086f2bcaa2a923fa3f7a3ff9aa3", size = 106099, upload-time = "2026-07-08T12:25:16.159Z" }, + { url = "https://files.pythonhosted.org/packages/c9/7b/2da9c74c1ed25a89cc4e1c8e007ea2eb4a0f1fafa3e70d757fe3242c5c5c/librt-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54a315caf843c8d77e388cadc56ea9ded569935ee2d2347d7ea94992e5aa6fa", size = 126934, upload-time = "2026-07-08T12:25:17.275Z" }, + { url = "https://files.pythonhosted.org/packages/d0/65/aead61bbf3b5358593f9d4779d2a0e88eaf6ec191a6342dde36dd1df6371/librt-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:c718e99a0992127af84385378460db624103b559ab260435abcfe77a4e4ed1c1", size = 112236, upload-time = "2026-07-08T12:25:18.425Z" }, + { url = "https://files.pythonhosted.org/packages/67/3b/18e7b63255297a2bdc9c25c8d6d4ca8eca9f63aceb1252c0f7427ac7099e/librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3", size = 151027, upload-time = "2026-07-08T12:25:19.638Z" }, + { url = "https://files.pythonhosted.org/packages/4d/68/e2248452c00d1a03b45fee1752cdc8f790a476efd2402b75181da88a9e61/librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c", size = 155152, upload-time = "2026-07-08T12:25:20.851Z" }, + { url = "https://files.pythonhosted.org/packages/0e/16/52b1c99bf19057a062aac39c900cbb81499f6f75d6c537c14463d247ba78/librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c", size = 502499, upload-time = "2026-07-08T12:25:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/9f/54/b811151805c795f55e0dedee6ec687b75f9982a8105d240ea3910737a77b/librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b", size = 496108, upload-time = "2026-07-08T12:25:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f8/094d6b2bd93f3fdaa54db54cc788c4a365333bddad65ab02e04da0b1d004/librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9", size = 531576, upload-time = "2026-07-08T12:25:24.648Z" }, + { url = "https://files.pythonhosted.org/packages/2e/40/541733d5755824f968f7ec39d78ffbd75d145964157ae5e69a09ec6d7326/librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db", size = 524390, upload-time = "2026-07-08T12:25:25.898Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b5/255673cfdbf5ba663339d36cd863c897289ab4337577e19f9405ce059f36/librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6", size = 543053, upload-time = "2026-07-08T12:25:27.436Z" }, + { url = "https://files.pythonhosted.org/packages/9e/11/ab5005e9c9850710f21e354201bf090646349d3fabf5f951eaf70235729e/librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7", size = 546387, upload-time = "2026-07-08T12:25:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/a2/04/a5d7ce1d1df1afd15ca283dcdf7530ac073e12d69ae8c40879dda96f7868/librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1", size = 535970, upload-time = "2026-07-08T12:25:30.171Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/927e267a6daa290174ac281b23c9804c8829b042ade9c6f24a065f540958/librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a", size = 573582, upload-time = "2026-07-08T12:25:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/10/24/b6c5213efe39c19f9e13605644d0cf063b4ddaa33ac2e45b088e23a70e2e/librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628", size = 82189, upload-time = "2026-07-08T12:25:32.675Z" }, + { url = "https://files.pythonhosted.org/packages/4c/00/d29736be177a906ac0b84a5b04b4fbfa22c776dc2f366de4172b0f968c08/librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927", size = 106193, upload-time = "2026-07-08T12:25:33.692Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ac/aff6fb45393cb8912f39dfb156ef6b2d1cadb207ff465fc8f66141054be8/librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650", size = 126962, upload-time = "2026-07-08T12:25:34.769Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127, upload-time = "2026-07-08T12:25:35.981Z" }, + { url = "https://files.pythonhosted.org/packages/7b/66/f49ae0d592bd45b6941e9a8bafcb6a87cddcd501ee7874707e767f01b585/librt-0.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:25218d94b1d2cbc0ba1d8a3f9dc9af578d9646e5ed16443a70cde1dfdcce6d71", size = 149818, upload-time = "2026-07-08T12:25:37.203Z" }, + { url = "https://files.pythonhosted.org/packages/3d/50/51c76d74014d04fb95b6506d286808984b78a2f7a41039094e6b2194ac48/librt-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f26629539d4893c2957a16c41bb058e1e135c1f150f6a2e25ed047f64cf3f5c6", size = 154071, upload-time = "2026-07-08T12:25:39.399Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fe/f19b0f5f82d5a1f2da736586bc840abd00ce07d6388136ae80b7333883fc/librt-0.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4517d47b2b8af26975a406fba7d314de9696d864252e0257c6ea90238cfe27f", size = 494168, upload-time = "2026-07-08T12:25:40.641Z" }, + { url = "https://files.pythonhosted.org/packages/94/bc/b8550c75775127fd31a5f20e8775997f7b527ad661fc8ddccd7497c064f7/librt-0.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f19e181de5b3a1148bb3420b8c4b0b0ea0fce6950099724ad151d6cea5acc180", size = 491054, upload-time = "2026-07-08T12:25:41.905Z" }, + { url = "https://files.pythonhosted.org/packages/30/14/4d0204867623df3f33f86efd3d3692ba5e01321443f4d6eab35a22697618/librt-0.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22034924f5b42d5a56371cf271771bfeaabf235a7a8b6264bef2d20013f786c6", size = 523006, upload-time = "2026-07-08T12:25:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/19/0a/c45fc9a260934696bace1ac5df1e148ac92bd71767aee3bf7cd7a4534f4c/librt-0.13.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7897db4e95e22468bdda33d8e012ceacd0182abf001e6389d763f0def6286b9", size = 515058, upload-time = "2026-07-08T12:25:44.541Z" }, + { url = "https://files.pythonhosted.org/packages/13/0a/50c5ce45b326854ef8fa6ae4c36cf5142e5c55315eaf9e51d0ae73ac4da3/librt-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ce61b3746545029d4f5c17d6bd74b676254ad98433086c846ffb5e8fa73f007", size = 534025, upload-time = "2026-07-08T12:25:45.825Z" }, + { url = "https://files.pythonhosted.org/packages/89/2d/08c413c8f93fc13b8103624fce38e5caa86cd08cbbc8465870ab287af54b/librt-0.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:46c330e82565962c761dbce7941be2cff7db674ee807455a8d0cadc5f9b759b0", size = 540557, upload-time = "2026-07-08T12:25:47.059Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/93af71fb4a364952210051811dd4e40174e79656b050c89cacac18af3330/librt-0.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:375f5af8f99cbaa99dd293af986e3d57caabc9ba81a5d3f021603764854197a1", size = 523201, upload-time = "2026-07-08T12:25:48.392Z" }, + { url = "https://files.pythonhosted.org/packages/c1/6e/9766f07b676a4889d9f8bc2864e9ba5fff165653143ef4dda7df6aa34d16/librt-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9320d34c3376ae204b2cd176e8d4883a013934e0aef822f1aed9c536490c275d", size = 565740, upload-time = "2026-07-08T12:25:49.678Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1e/664e3472ce2b6e10e9b83f29d4a36eb982ff6b5a169ae7567bba3a4c4ff5/librt-0.13.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9af313c66157a69dc69ea0059a66961692250e0dc95af9c385a48ffb770a0d16", size = 81611, upload-time = "2026-07-08T12:25:50.857Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d4/8582a4d65e2234673685e07309d02c230b28a85724eb0acbf13f019b7f6e/librt-0.13.0-cp314-cp314-win32.whl", hash = "sha256:f2a7253458e34f33543551394ae4fe104b497ec2a65ac266074de64c1df82e37", size = 100106, upload-time = "2026-07-08T12:25:52.03Z" }, + { url = "https://files.pythonhosted.org/packages/63/ce/0cb99efe6086b46cd985dc26672166fae312a239690e75871f7fafbd3fc5/librt-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:a3dfe4edf10e8ed7e55b026a8bfc2c2a8704218b659cd4bffdf604fab966dc39", size = 121209, upload-time = "2026-07-08T12:25:53.166Z" }, + { url = "https://files.pythonhosted.org/packages/26/85/4f3ccb083a3c9b0d42e223acdb3c3f507953324a59cdcab4826e8e2e3b89/librt-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:68a5faee4bba381cb93b5961f684a514cf0053cb92308ff9c792c2fea0b174c6", size = 106404, upload-time = "2026-07-08T12:25:54.253Z" }, + { url = "https://files.pythonhosted.org/packages/b2/77/333191499538c8e8189de7a4cba8e6f49ee949fd6d6e6324b21fd1522466/librt-0.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a38fb81d8376dfa2f8963b265fec07637802b0d01e2a127c19c66cb070fb24f5", size = 159231, upload-time = "2026-07-08T12:25:55.432Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9e/2aa83758f22c278b837a1d8025898434ce2b8bff36678d5330ecaef56dff/librt-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d4c8d9bd5abce34b2e75edb3bf37ab0f34e49b1f915a40ae8468eb7c85bc5b46", size = 161300, upload-time = "2026-07-08T12:25:56.585Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c0/86791e936553ca763d6b3c2fb4d31d596cd00e14fa631c283a40ba01559a/librt-0.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:387e2f1d27e89bffe0d3f520f0da0662c973fd607ca16c1808f8a5085419485e", size = 582056, upload-time = "2026-07-08T12:25:58.144Z" }, + { url = "https://files.pythonhosted.org/packages/a8/d3/a9ec15984a185e000c4d2a16ba28bd623124ad4c38a10974c7ff78e3a893/librt-0.13.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:4f6db193d2e5e0ed60359b9a5a682cd67205d0d3b1e459a867dd4b5c4e7eaa7a", size = 562758, upload-time = "2026-07-08T12:25:59.544Z" }, + { url = "https://files.pythonhosted.org/packages/3c/af/dbe36b78b19c06a55097f99305e4ea9458e2273e6ae16a3cbecaad7ee978/librt-0.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d38604854e8d22faadf683ec6c02bb0f886e2ba56ef981a1c36ee275f21ea22", size = 602095, upload-time = "2026-07-08T12:26:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a8/2966891b4dd2830f5203fbee92ac2c4947653a2390ba73dfa44244fad025/librt-0.13.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:371f7ce73026815dafd51c50ce38416e91428b28c4b2ec97cd39271164b0045c", size = 593452, upload-time = "2026-07-08T12:26:02.352Z" }, + { url = "https://files.pythonhosted.org/packages/61/f5/4df8bfc8405ecf8c0d525b4d69636f694bdd8620b313ec8b76e54a5926cc/librt-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3aaedf52171bee90860704c560bc798fe83b76247df47568e0197e9b13c735a0", size = 623729, upload-time = "2026-07-08T12:26:04.294Z" }, + { url = "https://files.pythonhosted.org/packages/d6/13/9ac202dffc8db06f75d06c08c2f9f6ff054be67d21272dcc078fa1cc0c57/librt-0.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96bad8725a4f196a798366c25ce075d1f7543a4ec045ffc13e6a7ec095cdab04", size = 617077, upload-time = "2026-07-08T12:26:05.845Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/ebe38610716aee5cb28efd95089bb90192096179802779381e1c5dcf239c/librt-0.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6bf6a559ffe4a93bbea6cf31ddf01a7fd9ba342ef51f27beb178e318b74acd61", size = 599561, upload-time = "2026-07-08T12:26:07.21Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5c/c2e72e236fff7abc716d5b1753b8b8cd3ea85ac46fe17d2e7c51d4e1c723/librt-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:301067672387902c55f94b51d5022304b36c966ea9fe1f21caab99a9bef487c9", size = 645511, upload-time = "2026-07-08T12:26:08.562Z" }, + { url = "https://files.pythonhosted.org/packages/0c/99/6203ce619dee940d6bfbe099ec3fe4be00a68e9d60f70abf906cf124fe66/librt-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:5fdcf34f86de8fb66d7dc7589f96ba91c4aa46671200d400e6fd6f109a483f18", size = 104357, upload-time = "2026-07-08T12:26:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/52/dd/843b6314087c41657c7036d7914d8f294bdf9b580aa8513ea0588c8e9a3d/librt-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:260c33e92263fa629b4f6d3c51967a1c2158fe6c33237aaa3ebeac586b085259", size = 126998, upload-time = "2026-07-08T12:26:10.975Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/3dcec2884ba1b0806d1408612555c38dd5d68e90156b59f75f6e36435c3a/librt-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2f281549a4c52ac7bb97997f14353f8bd0e53a34ca0dad1c905cfd0b4a58ae99", size = 110771, upload-time = "2026-07-08T12:26:12.303Z" }, +] + +[[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/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { 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 = "more-itertools" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, +] + +[[package]] +name = "mypy" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/7e/be536678c6ae49ef058aba4b483d8c7bc104f471479016066f345bc1f5f8/mypy-2.2.0.tar.gz", hash = "sha256:2cdd99d48590dce6f6b7f1961eda75386364398fcdaad86923bc0f0231bf9baf", size = 3950939, upload-time = "2026-07-08T01:37:27.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/f1/a818c375094e20a74ec51444cce6979cedd61d476f860c2f084ea8653bd2/mypy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97d21f3a1582ef30d0b77f9ca3ad88b4263f3c5aea9e7380cb5d7175160aec7e", size = 14903842, upload-time = "2026-07-08T01:33:27.944Z" }, + { url = "https://files.pythonhosted.org/packages/33/15/f332590488a8e4bb4a24036484c8bc750fe01fbef09701451a8790ac4c44/mypy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2529017942b951cc43db2e2712b71cc3aa7e9d90567630c03ed63d430aeec61a", size = 13902986, upload-time = "2026-07-08T01:36:59.24Z" }, + { url = "https://files.pythonhosted.org/packages/38/9f/7ae37860922d264b536790a3307caa195eb8abb733f2e81608229bbb0f07/mypy-2.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0823d7f273f3b4a49df7573eac271ddbf77a0e5f53e24d294a4f0f77ad22e1d7", size = 14132163, upload-time = "2026-07-08T01:34:16.111Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6d/25c31c85a793340a4a9be593f8fe2cea08fd7b615f8b602b3f52f2d822f6/mypy-2.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d78a0fc90704894bc9948d78affad14b882b08183a7c30412d185748aaa9418b", size = 15070110, upload-time = "2026-07-08T01:34:44.933Z" }, + { url = "https://files.pythonhosted.org/packages/cb/67/7adf1caa143f6a0d9c564de24e0c1ed48340185110c3b918adc2f447192b/mypy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6efab25fa3568569fda06928743382900860c48be9200c1758ef5ce94b532714", size = 15371554, upload-time = "2026-07-08T01:34:27.688Z" }, + { url = "https://files.pythonhosted.org/packages/49/cb/ceca928213076f540d9b068c3655c52d43549f124c1f6dece5ecb747bbeb/mypy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:daeaf5287091d68f674c91416cabab0f80f36e347ed17fded38d7ebde682e9d1", size = 11063618, upload-time = "2026-07-08T01:36:13.235Z" }, + { url = "https://files.pythonhosted.org/packages/41/24/08caa177a4f2e0c44b96d5ad85f2754761b45bb6d65841451ef5dc9edb86/mypy-2.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:f8d97940a0259e09c219903579720b2df12170c0c3a3b3799837c1fb63deb44e", size = 10070720, upload-time = "2026-07-08T01:37:13.251Z" }, + { url = "https://files.pythonhosted.org/packages/96/2e/1ea8028583fef6561d146b51d738d91268201313ecf69e603e808a740e74/mypy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea6fe1a30f3e7dc574d641497ffead2428046b0f8bc5804d13b4e4392fdc317b", size = 14739569, upload-time = "2026-07-08T01:35:33.202Z" }, + { url = "https://files.pythonhosted.org/packages/ba/67/c0607da57d78358b3b4fbfa90ee8ca5c6bd1dbb997c9648904ad4d8861d8/mypy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6965829a6d847a0925f51149472bbeb7a39332fb4801972fdfd9cedd9f8d43a4", size = 13821442, upload-time = "2026-07-08T01:33:45.991Z" }, + { url = "https://files.pythonhosted.org/packages/bb/56/0407007d4ec7a762bac20724af1f0a57a9d860186c39e73105b6b8396fa2/mypy-2.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a906bfc9c4c5de3ece6dc4726f8076d56ce9be1720baf0c6f84e926c10262a4", size = 14049813, upload-time = "2026-07-08T01:35:44.282Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d1/d718e006c8fed4bece7a1bebeea6dcd05f31433af552fc45a82fef426379/mypy-2.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91df92625cb3452758f27f4965b000fb05ad89b00c282cc3430a7bd6b0e5389e", size = 14978473, upload-time = "2026-07-08T01:36:54.064Z" }, + { url = "https://files.pythonhosted.org/packages/82/03/dae7299c45a84efb1590875f92652a5beb2da98a2cff9a567995e2583fe4/mypy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d42893d15894fd34f395090e2d78315ba7c637d5ee221683e02893f578c2f548", size = 15224649, upload-time = "2026-07-08T01:35:15.988Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8e/738e4e73d030f20852a81383c74319ca4a9a4fdaadc3a75823588fe2c833/mypy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3abaeec02cdc72e30a147d99eb81852b6b745a2c8d19607e25241d79b1abbce", size = 11049851, upload-time = "2026-07-08T01:34:39.31Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/6c58caedb3fa5831a9b179687cd7dc252c66a61dca4800e5ba19c8eff82b/mypy-2.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c39bdb7ceab252d15011e26d3a254b4aaa3bbf121b551febfa301df9b0c69abe", size = 10060307, upload-time = "2026-07-08T01:33:21.852Z" }, + { url = "https://files.pythonhosted.org/packages/d1/be/fbaba7b0ee89874fb11668416dec9e5585c190b676b0796cff26a9290fe8/mypy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:484a2be712b245ac6e89847141f1f50c612b0a924aa25917e63e6cfcf4da07cd", size = 14928025, upload-time = "2026-07-08T01:37:24.376Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8f/f79a7c5a76671b0f563d4beaa7d99fe90df4500d2c1d2ba1be0432121bcf/mypy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb0a020dc68480d40e484675558ed637140df1ccbf896a81ba68bca85f2b50a0", size = 13793027, upload-time = "2026-07-08T01:34:33.937Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b9/3db0086bab611d34e26061b86189e6f71de6d22a9b81699a93b006eabcf6/mypy-2.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc361340732ce7108fa0308812caf02bb6868f16112f1efd35bcad88badf3327", size = 14108322, upload-time = "2026-07-08T01:36:08.316Z" }, + { url = "https://files.pythonhosted.org/packages/58/29/4f1e13979a848de2a0fd385462354b58358b6e8b3d9661663e308f6e3d5d/mypy-2.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0179a3a0b833f724a65f22613607cf7ea941ab17ec34fa283f8d6dfe21d9fa9", size = 15190198, upload-time = "2026-07-08T01:36:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/14/f7/7759f6294d9d25d86671957d0974a215a2a24d429526e26a2f603de951c5/mypy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e0899b13da1e4ba44b880550f247402ce90ffecc71c54b220bcbe7ecb34f394", size = 15424222, upload-time = "2026-07-08T01:33:39.398Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1b/05b212bef4d2234b5f0b551ea53ce0680d8075b2e79861c765f70b590945/mypy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:511320b17467402e2906130e185abffffa3d7648aff1444fc2abb61f4c8a087d", size = 11135191, upload-time = "2026-07-08T01:35:03.019Z" }, + { url = "https://files.pythonhosted.org/packages/92/51/495e7122f6589948b36d3820a046461906756a0eb1b6dedc13ebfec7815e/mypy-2.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:7589f370b33dcdd95708f5340f13a67c2c49140957f934b42ef63064d343cca0", size = 10132502, upload-time = "2026-07-08T01:34:04.508Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5f/2d7a9ac5646274cd6e77ce3abcc2a9ece760c2b21f4c4b9f301711e07855/mypy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6968f27347ef539c443ddfd6897e79db525ddb8c856aa8fbf14c34f310ca5193", size = 14931618, upload-time = "2026-07-08T01:33:51.702Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8a/1adaa7caaa104f87021b1ac71252d62e646e9b623d77900ac7a0ae252bf3/mypy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3df226d2a0ae2c3b03845af217800a68e2965d4b14914c99b78d3a2c8ae23299", size = 13857718, upload-time = "2026-07-08T01:35:27.555Z" }, + { url = "https://files.pythonhosted.org/packages/1d/15/b11586b5aebbb82213e297fc30a6fcf3bed6a9deea3739cd8dd87621f3fd/mypy-2.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc53553996aca2094216ad9306a6f06c5265d206c1bcb54dd367560bd3557825", size = 14059704, upload-time = "2026-07-08T01:33:57.363Z" }, + { url = "https://files.pythonhosted.org/packages/03/db/071e05ab442596bdf7a845e830d5ef7128a0175281038245b171a6b16873/mypy-2.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:996abf2f0bebf572556c60720e8dc0cf5292b64060fa68d7f2bc9caa48e01b6f", size = 15128719, upload-time = "2026-07-08T01:33:33.855Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1c/c4b84eafb85ee315da72471523cc1bf7d7c42164085c42333601da7a8817/mypy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ec287c2381898c652bf8ff79448627fe1a9ee76d22005181fac7315a485c7108", size = 15378692, upload-time = "2026-07-08T01:34:10.468Z" }, + { url = "https://files.pythonhosted.org/packages/68/a4/59a0ee94877fdfe2958cec9b6add72a75393063c79cb60ab4026dd5e10c2/mypy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2c967a7685fa93fcf1a778b3ebe76e756b28ba14655f539d7b61ff3da69352a", size = 11150911, upload-time = "2026-07-08T01:35:21.603Z" }, + { url = "https://files.pythonhosted.org/packages/90/e4/6a9144be50180ed43d8c92de9b03dff504daa92b5bcc0353e8960799a23b/mypy-2.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:d59a4b80351ec92e5f7415fcdd008bd77fcbefc7adf9cbc7ffe4eb9f71617734", size = 10125389, upload-time = "2026-07-08T01:36:36.164Z" }, + { url = "https://files.pythonhosted.org/packages/73/32/0aa8d8d197023ca6040f7b25a486cb47037b6350b0d3bae657c8f85fb43f/mypy-2.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1f6c3d76853071409ac58fc0aadfb276a22af5f190fdaa02152a858088a39ebd", size = 14926083, upload-time = "2026-07-08T01:36:02.365Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7c/35bbe0cb10e6699f90e988e537aaf4282a6c16e37f58848a242eb0a98bde/mypy-2.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bdaa177e80cc3292824d4ef3670b5b58771ee8d57c290e0c9c89e7968212332c", size = 13879985, upload-time = "2026-07-08T01:36:31.093Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0c/1597fbebd873e9b63452317740ae3dd32692cec5da180cc65acd96cd28cf/mypy-2.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80923e6d6e7878291f537ee11052f974954c20cb569798429a5dc265eb780b47", size = 14076883, upload-time = "2026-07-08T01:34:21.789Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/2ec021a83ec01b5d522639f78d8b36adade7fa4821db0f48fd6d82e861f3/mypy-2.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f24bd465a09077c8d64be8f19a6646db467a55490fd315fe7871afe6bb9645", size = 15103567, upload-time = "2026-07-08T01:36:47.717Z" }, + { url = "https://files.pythonhosted.org/packages/53/3a/8cb3529f6d6800c7d069935e5c83a05d80263847b8a947cf6b0b16a9e958/mypy-2.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:db34595464869f474708e769413d1d739fc33a69850f253757b9a4cc20bc1fec", size = 15354641, upload-time = "2026-07-08T01:36:41.592Z" }, + { url = "https://files.pythonhosted.org/packages/d6/4d/320bc9a9553f8a9db5e847ec5ded762ef7ed7403c76c4ba2e8181c80e2f0/mypy-2.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b48092132c7b0ef4322773fecae62fc5b0bc339be348badeec8af502122a4a51", size = 7694355, upload-time = "2026-07-08T01:37:03.291Z" }, + { url = "https://files.pythonhosted.org/packages/90/05/bf3b349e2f885cd3aab488111bb9049439c28bc028dac5073350d3df8fbe/mypy-2.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:6fc0e98b95e31755ca06d89f75fafa7820fbb3ea2caace6d83cba17625cd0acb", size = 11329146, upload-time = "2026-07-08T01:35:38.318Z" }, + { url = "https://files.pythonhosted.org/packages/41/a5/558b06e6cfe17ab88bb38f7b370b6bc68a74ba177c9e138db9748e422d2d/mypy-2.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:bc73a5b4d40e8a3e6b12ef82eb0c90430964e34016a36c2aff4e3bfe37ba41f0", size = 10316586, upload-time = "2026-07-08T01:36:25.458Z" }, + { url = "https://files.pythonhosted.org/packages/0b/21/f0b96f19a9b8ba111a45ffbe9508e818b7f6990469b38f6888943f7bfd3a/mypy-2.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6257bd4b4c0ae2148548b869a1ff3758e38645b92c8fe65eca401866c3c551c1", size = 15922565, upload-time = "2026-07-08T01:35:56.282Z" }, + { url = "https://files.pythonhosted.org/packages/18/f2/1dbcb20b0865d5e992541450a8c73f2fcc90f8bd7d8a4b81313e16934870/mypy-2.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dfa22b3ae862ac1ce76f5976ddd402651b5f090bcfd49c6d0484b8983a29eaf8", size = 14816515, upload-time = "2026-07-08T01:34:58.167Z" }, + { url = "https://files.pythonhosted.org/packages/84/a2/18cce9c7d5b4d14010d1f13836da11b234dda917b17ca8671fc32c136997/mypy-2.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30a430bf26fe8cf372f3933fbd83e633d6561868803645a20e4e6d4523f52a3b", size = 15272246, upload-time = "2026-07-08T01:37:08.72Z" }, + { url = "https://files.pythonhosted.org/packages/ce/71/24d720c7924829bd675cbde2d0fa779f50abf676ca617f53d6a8bfef5fa7/mypy-2.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d58655a60e823b1a4c9ebcda072897fb0193c2f3e6f8e7c433e152aa4cb00233", size = 16226295, upload-time = "2026-07-08T01:34:51.861Z" }, + { url = "https://files.pythonhosted.org/packages/b7/5e/785730990fc863ad8340b4ab44ac4ca23270aecff92c180ccdf27f9f5869/mypy-2.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d4452c955caf14e28bb046cbd0c3671272e6381630a8b81b0da9713558148890", size = 16493275, upload-time = "2026-07-08T01:35:09.337Z" }, + { url = "https://files.pythonhosted.org/packages/93/33/55b1edf16f639f153972380d6977b81f65509c5b8f9c86b58b94b7990b03/mypy-2.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:68f5b7f7f755200f68c7181e3dfb28be9858162257690e539759c9f57721e388", size = 11749038, upload-time = "2026-07-08T01:35:50.071Z" }, + { url = "https://files.pythonhosted.org/packages/61/36/67424748a4e65e97f0e05bf00df379dfb6c2d817f82cc3a4ce5c96d99beb/mypy-2.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:78d201accfafce3801d978f2b8dbbd473a9ce364cc0a0dfd9192fe47d977e129", size = 10704254, upload-time = "2026-07-08T01:37:17.971Z" }, + { url = "https://files.pythonhosted.org/packages/28/cb/142c2097ca02c0d295b00625ff946808bdda65acda17d163c680d8a6a474/mypy-2.2.0-py3-none-any.whl", hash = "sha256:ecc138da861e932d1344214da4bae866b21900a9c2778824b51fe2fb47f5180e", size = 2726094, upload-time = "2026-07-08T01:34:00.075Z" }, +] + +[[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 = "orloj-sdk" +source = { editable = "." } +dependencies = [ + { name = "httpx" }, + { name = "pydantic" }, + { name = "typing-extensions" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage" }, + { name = "datamodel-code-generator" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-httpx" }, + { name = "pyyaml" }, + { name = "respx" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "httpx", specifier = ">=0.27,<1" }, + { name = "pydantic", specifier = ">=2.5,<3" }, + { name = "typing-extensions", specifier = ">=4.8" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7" }, + { name = "datamodel-code-generator", specifier = ">=0.26,<1" }, + { name = "mypy", specifier = ">=1.9" }, + { name = "pytest", specifier = ">=8" }, + { name = "pytest-asyncio", specifier = ">=0.23" }, + { name = "pytest-cov", specifier = ">=4" }, + { name = "pytest-httpx", specifier = ">=0.30" }, + { name = "pyyaml", specifier = ">=6" }, + { name = "respx", specifier = ">=0.21" }, + { name = "ruff", specifier = ">=0.3" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + +[[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 = "pydantic" +version = "2.13.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/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +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/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.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/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "pytest-httpx" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/42/f53c58570e80d503ade9dd42ce57f2915d14bcbe25f6308138143950d1d6/pytest_httpx-0.36.2.tar.gz", hash = "sha256:05a56527484f7f4e8c856419ea379b8dc359c36801c4992fdb330f294c690356", size = 57683, upload-time = "2026-04-09T13:57:19.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/55/1fa65f8e4fceb19dd6daa867c162ad845d547f6058cd92b4b02384a44777/pytest_httpx-0.36.2-py3-none-any.whl", hash = "sha256:d42ebd5679442dc7bfb0c48e0767b6562e9bc4534d805127b0084171886a5e22", size = 20315, upload-time = "2026-04-09T13:57:18.587Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.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/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { 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 = "respx" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/98/4e55c9c486404ec12373708d015ebce157966965a5ebe7f28ff2c784d41b/respx-0.23.1.tar.gz", hash = "sha256:242dcc6ce6b5b9bf621f5870c82a63997e8e82bc7c947f9ffe272b8f3dd5a780", size = 29243, upload-time = "2026-04-08T14:37:16.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/4a/221da6ca167db45693d8d26c7dc79ccfc978a440251bf6721c9aaf251ac0/respx-0.23.1-py2.py3-none-any.whl", hash = "sha256:b18004b029935384bccfa6d7d9d74b4ec9af73a081cc28600fffc0447f4b8c1a", size = 25557, upload-time = "2026-04-08T14:37:14.613Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/36/6f65aa9989acdec45d417192d8f4e7921931d8a6cf87ac74bce3eed98a8e/ruff-0.15.21.tar.gz", hash = "sha256:d0cfc841c572283c36548f82664a54ce6565567f1b0d5b4cf2caac693d8b7500", size = 4769401, upload-time = "2026-07-09T20:01:34.005Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/c6/ede15cac6839f3dbce52565c8f5164a8210e669c7bc4decb03e5bdf47d0d/ruff-0.15.21-py3-none-linux_armv6l.whl", hash = "sha256:63ea0e965e5d73c90e95b2434beeafc70820536717f561b32ab6e777cb9bdf5d", size = 10854342, upload-time = "2026-07-09T20:00:53.998Z" }, + { url = "https://files.pythonhosted.org/packages/28/9d/d825b07ee7ea9e2d61df92a860033c94e06e7300d50a1c2653aac27d24fe/ruff-0.15.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0f212c5d7d54c01bbfe6dcab02b724a39300f3e34ed7acbe995ccb320a2c58bd", size = 11139539, upload-time = "2026-07-09T20:00:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/f5/de/3b107712e642f063c7a9e0887c427b22cb44097de5aab36c05f2e280670c/ruff-0.15.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e6312e41bc96791299614995ea3a977c5857c3b5662b1ecef6755b02b87cb646", size = 10595437, upload-time = "2026-07-09T20:01:00.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/6f/b4523cc90ba239ede441447a19d0c968846a3012e5a0b0c5b62831a3d5e3/ruff-0.15.21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01d65b4831c6b2a4ba8ee6faa84049d44d982b7a706e622c4094c509e51673be", size = 10990053, upload-time = "2026-07-09T20:01:02.187Z" }, + { url = "https://files.pythonhosted.org/packages/92/cc/c6a9872a5375f0628875481cf2f66b13d7d865bf3ca2e57f91c7e762d976/ruff-0.15.21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c5a913a589120ce67933d5d05fd6ddbcc2481c6a054980ee767f7414c72b4fd", size = 10666096, upload-time = "2026-07-09T20:01:04.299Z" }, + { url = "https://files.pythonhosted.org/packages/ab/97/c621f7a17e097f1790fa3af6374138823b330b2d03fc38337945daca212c/ruff-0.15.21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef04b681d02ad4dc9620f00f83ac5c22f652d0e9a9cfe431d219b16ad5ccc41", size = 11537011, upload-time = "2026-07-09T20:01:06.771Z" }, + { url = "https://files.pythonhosted.org/packages/ea/51/d928727e476e25ccc57c6f449ffd80241a651a973ad949d39cfb2a771d28/ruff-0.15.21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16d090c0740916594157e75b80d666eab8e78083b39b3b0e1d698f4670a17b86", size = 12347101, upload-time = "2026-07-09T20:01:08.859Z" }, + { url = "https://files.pythonhosted.org/packages/1e/88/8cd62026802b16018ad06931d87997cf795ba2a6239ab659606c87d96bf0/ruff-0.15.21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a10e74757dd65004d779b73e2f3c5210156d9980b41224d50d2ebcf1db51e67", size = 11572001, upload-time = "2026-07-09T20:01:11.092Z" }, + { url = "https://files.pythonhosted.org/packages/b2/97/f63084cf55444fc110e8cb985ebfcc592af47f597d44453d778cb81bc156/ruff-0.15.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bab0905d2f29e0d9fbc3c373ed23db0095edaa3f71f1f4f519ec15134d9e85c8", size = 11549239, upload-time = "2026-07-09T20:01:13.27Z" }, + { url = "https://files.pythonhosted.org/packages/9d/77/f107da4a2874b7715914b03f09ba9c54424de3ff8a1cc5d015d3ee2ce0ac/ruff-0.15.21-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:00eca240af5789fec6fe7df74c088cc1f9644ed83027113468efba7c92b94075", size = 11535340, upload-time = "2026-07-09T20:01:15.206Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e9/601deb322d3303a7bf212b0100ead6f2ee3f6a044d89c30f2f92bf83c731/ruff-0.15.21-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262ab31557a75141325e32d3357f3597645a7f084e732b6b054dde428ecd9341", size = 10964048, upload-time = "2026-07-09T20:01:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2e/0f2176d1e99c15192caea19c8c3a0a955246b4cb4de795042eeb616345cd/ruff-0.15.21-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:659c4e7a4212f83306045ec7c5e5a356d16d9a6ef4ae0c7a4d872914fc655d9d", size = 10667055, upload-time = "2026-07-09T20:01:19.73Z" }, + { url = "https://files.pythonhosted.org/packages/48/60/abd74a02e0c4214f12a68becfd30af7165cfdcb0e661ecdc60bbb949c09a/ruff-0.15.21-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9e866eab611a5f959d36df2d10e446973a3610bc42b0c15b31dc27977d59c233", size = 11242043, upload-time = "2026-07-09T20:01:21.947Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c6/583075d8ccabb4b229345edcaf1545eb3d8d6be90f686a479d7e94088bbf/ruff-0.15.21-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e89bc93c0d3803ba870b55c29671bad9dc6d94bb1eb181b056b52eb05b52854f", size = 11648064, upload-time = "2026-07-09T20:01:24.023Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3c/37d0ecb729a7cc2d393ea7dce316fc585680f35d93b8d62139d7d0a3700c/ruff-0.15.21-py3-none-win32.whl", hash = "sha256:01f8d5be84823c172b389e123174f781f9daf86d6c58719d603f941932195cdd", size = 10896555, upload-time = "2026-07-09T20:01:26.941Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b8/e43466b2a6067ce91e669068f6e28d6c719a920f014b070d5c8731725de3/ruff-0.15.21-py3-none-win_amd64.whl", hash = "sha256:d4b8d9a2f0f12b816b50447f6eccb9f4bb01a6b82c86b50fb3b5354b458dc6d3", size = 12038772, upload-time = "2026-07-09T20:01:29.497Z" }, + { url = "https://files.pythonhosted.org/packages/dd/75/e90ab9aeece218a9fc5a5bc3ec97d0ee6bb3c4ff95869463c1de58e29a1c/ruff-0.15.21-py3-none-win_arm64.whl", hash = "sha256:6e83115d4b9377c1cbc13abf0e051f069fab0ef815ea0504a8a008cee24dd0a8", size = 11375265, upload-time = "2026-07-09T20:01:31.772Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "typeguard" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240, upload-time = "2026-05-14T12:59:40.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748, upload-time = "2026-05-14T12:59:39.473Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[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" }, +]