diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 280801786..c8c90eead 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,6 +31,35 @@ jobs:
- name: Public tree audit
run: bash scripts/audit_public_tree.sh
+ documentation-history:
+ if: github.event_name == 'pull_request'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - name: Audit documents in every new commit
+ env:
+ DOCUMENTATION_BASE: ${{ github.event.pull_request.base.sha }}
+ run: bash scripts/audit_public_tree.sh --history-base "$DOCUMENTATION_BASE"
+
+ sandbox-contracts:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - name: Install optional runtime and practice
+ run: python -m pip install -e '.[dev,sandbox]' -e practices/h_cluster
+ - name: SDK contracts without allocation
+ run: python scripts/sandbox_sdk_contract.py
+ - name: Optional lifecycle and practice tests
+ run: python -m pytest tests/harborrl/test_sandbox_cleanup.py practices/h_cluster/tests
+
package:
runs-on: ubuntu-latest
needs: cpu
diff --git a/.gitignore b/.gitignore
index 2f4e7f480..7755b672b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,20 @@ harbor_datasets/
.tools/
.worktrees/
benchmarks/harbor/0.5-mvp/
-docs/
+docs/*
+!docs/agents/
+docs/agents/*
+!docs/agents/environment-check.md
+!docs/configuration-wizard.md
+!docs/deployment.md
+!docs/sandbox-rollout.md
+!docs/train-inference-consistency.md
+!docs/web-console.md
+practices/**/VALIDATION.md
+practices/**/HISTORICAL_VALIDATION.md
+integration-report-*.md
+integration-roadmap-*.md
+*.bundle
local/
configs/site/
examples/**/private*
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 98b03e866..46f7e2791 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,6 +13,25 @@ HarborRL accepts changes through pull requests from a public branch.
6. Submit a reviewable commit series and describe external versions, task
provenance, and any acceptance evidence.
+## Public documentation
+
+Commit documentation that helps users install, configure, run or troubleshoot
+HarborRL. Keep development plans, integration reports, experiment logs and
+machine-specific validation records outside the repository. Summarize current
+support and limitations in the relevant user guide without development history.
+
+The public-document allowlist is maintained in `scripts/audit_public_tree.sh`.
+Update it deliberately when adding a user guide. Check both the final tree and
+every new commit before opening a pull request:
+
+```bash
+bash scripts/audit_public_tree.sh --history-base origin/main
+```
+
+Deleting a private document in a later commit does not remove it from history.
+Clean unpublished commits before pushing; do not merge a development branch
+whose history contains private documents into a public branch.
+
Configuration and task fixtures must use generic placeholders. Absolute personal
paths, internal DNS names, private registry references, and unexplained large
files will fail review.
diff --git a/NOTICE.md b/NOTICE.md
index 4dc4f09ac..3ab8ecf6f 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -2,6 +2,10 @@
HarborRL is MIT-licensed by Princeton-AI.
+The local web console bundles Lucide 1.61.0 (ISC license). Its unmodified browser
+distribution and license are in `harborrl/webui/static/lucide.min.js` and
+`harborrl/webui/static/lucide-LICENSE.txt`. No CDN connection is required.
+
The public package does not vendor Slime, Megatron-LM, SGLang, Harbor, Claude
Code, or model weights. Operators are responsible for reviewing the licenses of
the exact external versions they pin and for complying with model and dataset
diff --git a/README.md b/README.md
index cc82cddd7..1b1b8ac7d 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
-
+
A lightweight agentic reinforcement learning framework for everyone
@@ -25,6 +25,8 @@
## News
+- **2026-10-26** Added an [offline configuration wizard](docs/configuration-wizard.md) and a [local Web console](docs/web-console.md) for configuration, validation, dry-runs, training launch, and logs.
+- **2026-10-26** Added [controller/GPU deployment configuration](docs/deployment.md), optional [OpenSandbox execution](docs/sandbox-rollout.md), and opt-in [train/inference consistency checks](docs/train-inference-consistency.md).
- **10/2026** 📣📣📣 HarborRL released
## About
@@ -40,6 +42,14 @@ HarborRL supports flexible combinations of **Models × Algorithms × Tasks × Ha
## AI-assisted Configure
+For the offline options catalog, interactive configuration, and answer-file setup,
+see the [configuration CLI guide](docs/configuration-wizard.md).
+Start with `harborrl options` or `harborrl init --help`.
+
+Run `harborrl web` to open the local browser console for configuration, validation,
+dry-runs, training launch, and logs. See the [web console guide](docs/web-console.md)
+for remote-controller access and lifecycle details.
+
If you want an AI agent to help deploy this repository and verify training,
ask it to first read and strictly follow
[docs/agents/environment-check.md](docs/agents/environment-check.md).
@@ -78,6 +88,12 @@ Confirm the result yourself against this checklist:
## Quick Start
+The default CPU install does not need a Sandbox SDK or cluster account.
+For controller placement, see [deployment](docs/deployment.md). For optional
+standard OpenSandbox execution, see [Sandbox rollout](docs/sandbox-rollout.md).
+The independently installed [H-cluster practice](practices/h_cluster/README.md)
+contains Brainbox-specific deployment and restricted-network relay examples.
+
### Install
Use Python 3.10 or newer. Runtime dependencies, development dependencies, and the `harborrl` command are locked by [pyproject.toml](pyproject.toml) and [uv.lock](uv.lock). The CPU development install does not require CUDA, Docker, model weights, Slime, Megatron-LM, or SGLang.
@@ -128,7 +144,7 @@ The command style is a shorthand over `config.yaml` in the working directory (or
## Task Configuration
-Training is configured with one YAML file that has a fixed section and field set. Relative paths resolve against the YAML file, and repeated `--set section.field=value` overrides are parsed as YAML values and validated again. Start site-specific configurations from [examples/native/train_qwen_native.yaml](examples/native/train_qwen_native.yaml).
+Training is configured with one YAML file that has a fixed section and field set. Relative paths resolve against the YAML file, and repeated `--set section.field=value` overrides are parsed as YAML values and validated again. Start site-specific configurations from [examples/native/train_qwen_native.yaml](examples/native/train_qwen_native.yaml). Optional [train/inference consistency checks](docs/train-inference-consistency.md) compare frozen rollout scores with the pre-update trainer and probe synchronized inference engines.
The configuration has three broad parts: `execution` is fixed to `harbor_job`; `harbor`, `gateway`, and `deployment` describe rollout workers, service endpoints, and GPU topology; `tasks`, `harness`, `model`, `training`, `sampling`, and `output` describe the experiment and its outputs.
diff --git a/README_zh.md b/README_zh.md
index 18a33b25c..f75e744a9 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -25,6 +25,8 @@
## News
+- **2026-10-26** 新增[离线配置向导](docs/configuration-wizard.md)和[本地 Web 控制台](docs/web-console.md),支持配置、校验、dry-run、训练启动和日志查看。
+- **2026-10-26** 新增 [controller/GPU 部署配置](docs/deployment.md)、可选的 [OpenSandbox 执行能力](docs/sandbox-rollout.md),以及按需启用的[训练与推理一致性检查](docs/train-inference-consistency.md)。
- **10/2026** 📣📣📣 HarborRL released
## 项目介绍
@@ -43,6 +45,11 @@ HarborRL 支持 **Models × Algorithms × Tasks × Harnesses (MATH)** 的灵活
如果你希望让 AI agent 辅助完成本 repo 的部署和训练验证,请让它先阅读并严格遵循
[docs/agents/environment-check.md](docs/agents/environment-check.md)。
+离线选项目录、交互式配置和 answer-file 配置方式见[配置 CLI 指南](docs/configuration-wizard.md)。
+可以从 `harborrl options` 或 `harborrl init --help` 开始。
+运行 `harborrl web` 可打开本地浏览器控制台,完成配置、校验、dry-run、训练启动和日志查看;
+远程访问与生命周期说明见 [Web 控制台指南](docs/web-console.md)。
+
推荐的实际操作流程是把下面这段提示词交给 agent,并把尖括号内容替换成你的机器信息:
```text
@@ -73,6 +80,10 @@ doctor 和 launch_native --dry-run。
## 快速开始
+默认 CPU 安装不需要 Sandbox SDK 或集群账号。
+controller 与 GPU 的部署方式见[部署指南](docs/deployment.md),可选的标准 OpenSandbox 执行方式见[Sandbox rollout 指南](docs/sandbox-rollout.md)。
+独立安装的 [H-cluster 实践包](practices/h_cluster/README.md)提供 Brainbox 部署与受限网络中继示例。
+
### 安装
使用 Python 3.10 或更新版本。Python 依赖、开发依赖和 `harborrl` 命令由 [pyproject.toml](pyproject.toml) 与 [uv.lock](uv.lock) 锁定。CPU 开发环境不需要 CUDA、Docker、模型权重、Slime、Megatron-LM 或 SGLang。
diff --git a/docs/configuration-wizard.md b/docs/configuration-wizard.md
new file mode 100644
index 000000000..441a6138b
--- /dev/null
+++ b/docs/configuration-wizard.md
@@ -0,0 +1,140 @@
+# Configuration CLI
+
+The configuration tools run with the core package (Python 3.10+ and PyYAML).
+They enumerate implemented capabilities, check configuration offline, and create
+consistent training schema 2 / deployment schema 1 files. They do not install
+software, download tasks or models, connect over SSH, or allocate Sandbox/GPU jobs.
+
+## Browse and validate
+
+```bash
+harborrl options
+harborrl options deployments --json
+harborrl options fields --json
+harborrl validate site/config.yaml --deployment site/deployment.yaml --json
+harborrl validate site/config.yaml --deployment site/deployment.yaml --check-files
+```
+
+Catalog dimensions are `models`, `algorithms`, `tasks` (task providers),
+`harnesses`, `datasets`, `deployments`, `fields`, and `compat`. JSON keeps the
+`catalog_version` and `schema_versions` envelope when selecting a dimension.
+The catalog reflects registered implementations, not roadmap claims. Dataset
+`status` describes support; `available` describes local task files. A wheel does
+not include repository examples, and planned datasets are not ready to train.
+
+Validation defaults to schema, semantics, and cross-file consistency. It does
+not open referenced profiles or tasks until `--check-files` is supplied. Run file
+checks on the controller: its source/interpreter, profile, task catalog and
+Sandbox configuration belong there. Local GPU and runner paths are checked
+locally; SSH target paths are deferred. The shared root may be created by a later
+run and is not required to exist during configuration. Credentials are never read.
+
+Checks are `passed`, `failed`, or `deferred`. The report is `invalid` if a check
+fails, otherwise `valid_with_deferred_checks` while live checks remain. Exit codes
+are 0 for no failed checks, 1 for validation/creation failure, and 2 for CLI usage
+errors. `--json` writes one JSON object to stdout. Schema validators may report
+only their first error. Without `--deployment`, the existing CLI discovery of
+`./deployment.yaml` and embedded fallback is preserved and reported.
+
+GPU memory fit, model loading, live tokenizer fingerprints, package versions,
+connectivity and platform capacity are not established by offline validation.
+Sandbox image bindings are checked locally; trusted site adapters and credentials
+are checked later by runner preflight. Sandbox installation/profile timing
+compatibility is additionally checked by the existing training dry-run.
+
+## Prepare inputs
+
+1. Obtain Harbor-format tasks and run `harborrl hub inspect --tasks-dir /path/tasks
+ --dataset my-tasks --revision pinned-revision --run-dir /path/inspection`.
+ The resulting JSONL is an inspection report, not a training catalog. Build a
+ JSON list (or JSONL records with a `.jsonl` suffix) containing exactly `id`,
+ `revision`, `path`, `task_digest`, and `reward_profile` for each selected task.
+ Copy `path` and `task_digest` from a `NEEDS_PROBE` inspection record, assign a
+ unique stable `id`, retain the pinned revision, and explicitly set the verifier's
+ reward contract (for example `reward_profile: {key: reward}`). See
+ [the catalog example](../examples/native/catalog.jsonl). `INVALID` and
+ `UNSUPPORTED` inspection results cannot be made trainable by changing labels.
+ Do not replace computed digests with placeholder values.
+2. Prepare a pinned Claude Code profile. See
+ [the native examples](../examples/native) for profile structure. It must match
+ the agent and task requirements of your deployment.
+3. Stage checkpoint and reference directories on the GPU target. In its existing
+ training environment, load the actual local tokenizer with the same options as
+ the runtime and call `harborrl.rollout.runtime.tokenizer_fingerprint(tokenizer)`.
+ Its return values are `tokenizer_digest, template_digest`. This is an explicit
+ preparation step, not an operation performed by the wizard. Hashing a filename
+ or guessing an arbitrary 64-character value does not establish compatibility.
+4. Identify controller/GPU/runner Python executables, checkout paths, SSH aliases
+ and shared storage. Service origins must include their declared ports and be
+ reachable by the participating machines.
+5. For Sandbox, prepare the existing platform configuration and image bindings.
+ H-cluster users follow [the optional H practice](../practices/h_cluster/README.md)
+ and reference its site file. The core CLI never asks for H project/environment
+ identifiers or embeds private endpoints or credentials.
+
+## Generate
+
+```bash
+harborrl init --out-dir ./site-new
+harborrl init --answers my-answers.yaml --out-dir ./site-new
+```
+
+Interactive mode asks for deployment roles, existing inputs, model fingerprints,
+GPU layout and a few sampling settings, then previews both configurations and
+requires `yes` to publish. Entering no or EOF cancels. Non-terminal use requires
+`--answers`; missing required answers never fall back to prompting.
+
+Start from one of these complete answer templates and replace every `REPLACE_*`
+value and example path before running:
+
+- [embedded-local](../examples/options/embedded-local.answers.yaml): one machine,
+ local Harbor runner, colocated training/serving by default.
+- [service-ssh](../examples/options/service-ssh.answers.yaml): controller, SSH GPU
+ target and explicit SSH Docker workers.
+- [service-sandbox-local-runner](../examples/options/service-sandbox.answers.yaml):
+ controller and runner here, SSH GPU target, Sandbox task execution.
+
+Templates deliberately contain invalid digest placeholders. They cannot claim
+training readiness without your real inputs. Default one-GPU colocate is a
+configuration starting point, not a Qwen model memory-fit recommendation.
+
+Answers allow only `answers_version: 1`, `preset`, `training`, `deployment`, and
+optional `sandbox_config`. Overrides use the existing schema section/field names;
+unknown keys fail. No expressions or shell commands are evaluated. Gateway fields,
+shared run root, GPU budget and worker destinations are derived when only one
+side is provided. Contradictory explicit values fail. Supply matching origin URLs
+when changing ports. Paths in training overrides are relative to the **final
+output directory**, not the answers file. Deployment machine paths are absolute.
+
+The Sandbox preset can also describe remote runners through an answers file:
+set `training.harbor.runner_transport: ssh` and provide deployment rollout workers
+with `docker: false`. The builder derives training worker destinations and the
+first worker's runner Python. All configured interpreters still require preflight.
+
+`--probe-local` explicitly enables bounded local `nvidia-smi` UUID and current
+Python version queries. It also reads explicit SSH Host aliases (no wildcard,
+Match execution or Include expansion) and gives a parent-directory access hint.
+Missing tools and failed commands return `unknown`, not zero GPUs. Suggestions
+never silently change answers; no remote probe or write permission test occurs.
+
+The output directory must not exist, and its parent must exist. Publication uses
+an exclusive per-target lock, same-parent staging and a directory rename. Existing
+files are never overwritten. Failures clean up only this invocation's staging and
+lock. After an abrupt process termination, inspect any remaining `.NAME.init.lock`
+and staging directory before removing them; automatic stale-lock recovery is not
+attempted. The lock coordinates wizard processes, not arbitrary external writers.
+
+The bundle contains `config.yaml`, `deployment.yaml`, and `setup-manifest.json`.
+The manifest records schema versions, configuration digests, references and the
+offline validation scope, including deferred checks. It does not copy credentials.
+
+The CLI prints these next commands without executing them:
+
+```bash
+harborrl validate site-new/config.yaml --deployment site-new/deployment.yaml --check-files
+harborrl train site-new/config.yaml --deployment site-new/deployment.yaml --dry-run
+harborrl doctor site-new/config.yaml --deployment site-new/deployment.yaml
+```
+
+`doctor` performs live environment/connectivity checks. Actual training remains a
+separate action. Command examples use POSIX shell quoting.
diff --git a/docs/deployment.md b/docs/deployment.md
new file mode 100644
index 000000000..2f35a8501
--- /dev/null
+++ b/docs/deployment.md
@@ -0,0 +1,110 @@
+# Deployment configuration and the controller placement
+
+A `deployment.yaml`
+describes where the manager, GPU training target and rollout workers live and
+how they reach each other; the training YAML keeps every training-side field
+(learning rate, sampling, task catalog) and is unchanged.
+
+```bash
+harborrl train train.yaml --deployment deployment.yaml
+```
+
+Without `--deployment`, a `deployment.yaml` in the working directory is picked
+up; when no file exists the compatible embedded-local topology is used and the
+legacy same-machine behavior is preserved bit-for-bit (recorded in the plan as
+`deployment.source: embedded-local`).
+
+## What the schema covers (v1)
+
+- `manager` — the HarborRL controller node: `embedded` or `service` mode, bind
+ address, control/gateway ports and the URLs other machines use to reach them.
+ In `service` mode both URLs must be plain origins that match their ports and
+ must not be loopback.
+- `gpu_target` — the Slime training machine: `local` or `ssh` transport, the
+ checkout (`source_dir`), training interpreter (`train_python`), an optional
+ controlled `environment_file` (a JSON variable map, never a shell), and
+ `expected_gpus`.
+- `storage` — `shared_fs` mode with the shared `run_root` and writability
+ expectations per role.
+- `rollout` — `ssh_docker` workers: SSH destination, runner interpreter,
+ Docker requirement and task roots. The `embedded` provider keeps worker
+ discovery in the training config.
+- `connectivity` — timeouts and which control-loop hops are required.
+
+Secrets are rejected by the schema: no passwords, keys or API tokens. SSH
+destinations resolve through the invoking user's SSH config, agent or a
+restricted identity file. Paths are interpreted in target-machine semantics —
+the manager never assumes its own `sys.executable` works on the GPU or workers.
+See `examples/native/deployment-embedded.yaml` and
+`examples/native/deployment-service.yaml`.
+
+## Preflight graph
+
+`train` and `doctor` share one check graph, executed at the earliest launch
+stage. Each check records the role and machine it ran against, the outcome,
+timing, and a user-executable fix. Any failed check exits nonzero before a run
+directory or training process is created.
+
+Layer order: manager local state (run root, token file, ports) → GPU target
+(SSH reachability, paths, model paths, visible GPU budget) → rollout workers
+(SSH, runner Python >= 3.12, pinned Harbor version, Docker, task roots) →
+control-loop addresses (DNS resolution now, live health dials after components
+start). `--dry-run` parses both configs and prints the resolved plan without
+any remote probing or side effects.
+
+When the GPU target is remote, machine-local dependency probes (Ray, Torch,
+Slime imports, SGLang image, `nvidia-smi`) do not run on the manager: they are
+enforced fail-closed by `slime_train.sh` at GPU job startup. The manager CPU
+needs no GPU dependencies.
+
+## TrainingTarget
+
+`harborrl/deployment/training_target.py` launches one Slime job either as a
+local child (legacy path) or over SSH (service mode). The SSH target writes a
+generated `gpu-launcher.sh` into the shared run directory, starts it detached
+(`setsid`) on the GPU machine, records the launcher pid, polls the shared
+`exit-code` record, and cancels by signalling the remote process group
+(INT → TERM → KILL). It never resubmits or restarts a job.
+
+## RolloutService (manager mode)
+
+`harborrl/rollout/service.py` is the manager-side control plane: HTTP JSON
+with run-scoped tokens.
+
+- `GET /healthz`, `GET /readyz`
+- `POST /runs/{run_id}/register` — idempotent by manifest digest; the same id
+ with a different manifest is a 409.
+- `POST /runs/{run_id}/groups` — idempotent group submission. Same group id
+ and content returns the existing state; same id with different content is a
+ 409; submitting against a non-admitted policy version is a 409. On timeout
+ the response says `running` and points at the status endpoint — the client
+ must query, never resubmit.
+- `GET /runs/{run_id}/groups/{group_id}`, `POST .../cancel`
+- `POST /runs/{run_id}/policy/activate` — validates the whole serving pool is
+ healthy on the expected version, drains old admission, then opens the new
+ version and publishes `policy-pool.json` into the shared run directory.
+- `POST /runs/{run_id}/serving/announce` — the GPU job publishes its inference
+ endpoint; the manager never hand-configures it.
+- `POST /runs/{run_id}/drain`, `POST /runs/{run_id}/cancel`
+
+Bulky data (trajectory manifests, token ids, logprobs, loss masks, group
+export) is written to the shared run directory. HTTP responses carry paths
+plus SHA-256 digests; `GroupResult` also carries per-slot identity, finish
+reason, reward, trace seal status and verifier digest.
+
+The GPU-side adapter (`harborrl/backends/slime_v032/rollout_service_client.py`)
+recomputes every manifest digest, checks identities against the request,
+requires a complete group of at least two slots on the locked policy version,
+and only then converts trajectories into Slime samples. It is selected by
+`HARBORRL_ROLLOUT_SERVICE_URL` in the GPU job environment.
+
+## Operational limits
+
+Service mode requires shared storage visible at the configured paths on the
+controller, GPU target and runners. A successful dry run checks configuration;
+it does not establish network reachability, available capacity or training
+quality. Check connectivity with doctor and inspect run artifacts after launch.
+
+Choose worker and group concurrency for the target deployment. Instance
+readiness does not imply that an agent is running, and a completed checkpoint
+does not establish learning improvement.
diff --git a/docs/sandbox-rollout.md b/docs/sandbox-rollout.md
new file mode 100644
index 000000000..6fdc9d19a
--- /dev/null
+++ b/docs/sandbox-rollout.md
@@ -0,0 +1,72 @@
+# Sandbox rollout
+
+Sandbox is an optional execution environment under the common Controller,
+RolloutService, Runtime and Harbor Runner. Docker execution remains the default.
+The core does not import the SDK until an explicitly selected runner needs it.
+
+## Standard OpenSandbox
+
+Use Python 3.12 or newer for the runner and install `pip install -e '.[sandbox]'`.
+Start with `examples/native/sandbox.yaml`. Set the service origin (without a
+trailing /v1), a private credential-file reference and exact task image bindings.
+Standard credentials are opaque tokens, not a platform-specific AK:SK format.
+
+Select the environment in your training config:
+
+```yaml
+harbor:
+ python: /opt/harborrl/runner/bin/python
+ version: 0.23.0
+ runner_transport: local
+ workers: []
+ environment:
+ runtime: sandbox
+ config: /srv/harborrl/site/sandbox.yaml
+```
+
+For service placement, use `rollout.provider: sandbox` in the deployment file.
+A local runner requires an empty deployment worker list. SSH runners require
+explicit workers, the same shared task/evidence paths and the adapter installed
+on each runner. Runner placement is independent of the environment provider.
+The target and route must be validated before claiming that an SSH/Sandbox
+deployment works on a real platform.
+
+The standard adapter uses Harbor's OpenSandbox implementation and adds durable
+allocation tracking, bounded lifecycle operations and cleanup-debt records.
+Custom trusted adapters subclass ManagedOpenSandboxEnvironment and are selected
+through `adapter: module:Class`; arbitrary task input cannot select code.
+Provider settings belong in `options` and per-image binding `options`.
+They must not contain inline secrets. Core plan construction does not import
+the adapter, read credentials, connect to a platform or allocate instances.
+
+The optional `installation` block supports a pinned offline Claude binary:
+`mode: offline_binary`, `version`, `binary`, and its 64-character `sha256`.
+Without it, Harbor performs normal agent setup, which can need network access.
+
+## Concurrency and evidence
+
+`max_live_instances` bounds all active instances; each image binding has its
+own limit. Binding capacity is acquired before global capacity to avoid
+starvation. `startup_concurrency` separately bounds simultaneous SDK startup
+using process-shared locks. Shared-filesystem flock support is required when
+runners use the same lock directory across hosts.
+
+Groups use the same identity, policy barrier, verifier and consumption audit
+as Docker. Failed slots cancel their group siblings; submitted independent
+groups finish before a partial batch is rejected. Unconfirmed cleanup blocks
+new admission. Production export never accepts synthetic evidence.
+
+## Checks
+
+`python scripts/sandbox_sdk_contract.py` checks installed APIs and fake lifecycle
+cleanup without contacting a platform. `sandbox_probe` checks the selected
+adapter, SDK, credential-file permissions and optional binary locally.
+Its explicit `--network` mode is adapter-specific and reports skipped checks.
+
+`python scripts/sandbox_smoke.py CONFIG --output NEW_DIRECTORY --gateway URL`
+is a live allocation test and consumes platform resources. A successful CPU
+test or dependency probe does not establish live training acceptance.
+
+For Brainbox and developer-mediated networking, see the independently
+installable [H-cluster practice](../practices/h_cluster/README.md). H-specific
+configuration from the experimental branch must be migrated as described there.
diff --git a/docs/train-inference-consistency.md b/docs/train-inference-consistency.md
new file mode 100644
index 000000000..f4bd4d2d2
--- /dev/null
+++ b/docs/train-inference-consistency.md
@@ -0,0 +1,79 @@
+# Train / inference consistency
+
+Consistency checks compare log probabilities of the **same response tokens at
+the same prefix**, before an optimizer update. This measures backend drift;
+it does not measure model quality or full-distribution KL divergence.
+
+The three policies are the observed behavior `q`, frozen pre-update trainer
+`p0`, and current trainer `pθ`. Backend drift is `log p0 - log q`; optimization
+drift is `log pθ - log p0`. The Native loss remains unchanged. No TIS, clipping
+correction, or new training objective is introduced.
+
+## Configuration
+
+Native schema 2 accepts an optional `consistency` mapping. Its default mode is
+`off`. Existing configurations remain valid. `audit` records numerical violations;
+`enforce` rejects violations before
+training or serving admission. Invalid evidence always rejects, including in
+audit mode. Enforce requires an explicit `max_abs_delta`. Calibrate tolerances
+for the actual model, precision, parallel layout and kernels.
+
+```yaml
+consistency:
+ mode: audit
+ max_abs_delta: null
+ max_mean_abs_delta: null
+ bad_token_delta: null
+ max_bad_fraction: 0.0
+ worst_tokens: 8
+```
+
+Native overrides use the existing `--set consistency.mode=audit` interface.
+For enforce, configure a deployment-specific `max_abs_delta` in YAML before
+loading the configuration. The configuration is preserved in launch evidence.
+
+## Native Megatron / SGLang
+
+The enabled launcher runs `harborrl.consistency.train`, which uses the pinned
+Slime training loop and its supported actor factory. It never edits upstream
+Slime. The existing pre-update forward supplies actual packed-batch `p0` values.
+Immutable serving sidecars bind token IDs, float32-normalized behavior scores,
+response masks, policy version, tokenizer and template identity. Missing or
+mismatched sidecars reject the batch.
+
+Before each weight synchronization, the actor evaluates two fixed token
+fixtures using Megatron. Every synchronized SGLang engine then teacher-forces
+those exact sequences. Generation-time and post-probe versions must both match.
+Each engine must satisfy the numerical limits independently. Checks also cover
+the final sync, even when no subsequent rollout occurs. Probe records are reused
+only for the same pool, configuration and immutable reference. A fixed canary
+is a smoke check; actual rollout-batch checks provide broader coverage.
+
+Supported initial scope: dense models, synchronous full NCCL synchronization,
+PP=CP=1, with TP and DP. Temperature and top-p must be 1; top-k must be disabled.
+Critic, old-actor queues, MoE, release-train and routing replay are rejected.
+Transformed sampling would require a separate definition of behavior scores.
+
+`RUN_DIR/consistency` contains sync references, pool admissions, serving-turn
+sidecars and `batch-*.json` reports. References include a bounded sampled
+parameter fingerprint per rank; this is **not** a full weight checksum. Reports
+contain valid-token-weighted absolute/signed errors, log-ratio range, bad-token
+fraction, bounded worst positions and histogram percentile **upper bounds**.
+They do not copy raw prompt or response text. TP duplicate counts are excluded.
+Distributed validation collects rank errors before all ranks reject.
+
+## Validation and limitations
+
+Run `pytest -q tests/test_consistency.py` for the dependency-free checks. With
+Torch installed, run `pytest -q tests/test_consistency_torch.py` for two-rank
+failure propagation. These tests do not run a separate training loop.
+
+GPU acceptance must additionally exercise the locked Megatron / SGLang versions
+on the target hardware: initial sync, actual rollout tokens, optimizer update,
+final sync and checkpoint resume. Record dtype, attention backend, TP/DP, model
+revision and measured error distribution. CPU tests and mocked HTTP responses
+cannot certify SGLang input-logprob behavior or CUDA numerical tolerances.
+This implementation has not yet passed that live GPU acceptance gate.
+
+H-cluster relay topology remains a deployment practice. No H-cluster hostnames,
+private paths or machine-specific numerical thresholds belong in this module.
diff --git a/docs/web-console.md b/docs/web-console.md
new file mode 100644
index 000000000..3d9603ccc
--- /dev/null
+++ b/docs/web-console.md
@@ -0,0 +1,110 @@
+# Local web console
+
+```bash
+harborrl web --workspace ./harborrl-web
+```
+
+This opens a browser on an available loopback port. Keep the terminal running.
+`--port 8765` chooses a fixed port; `--no-browser` prints the URL without opening
+it. The workspace stores saved configuration bundles and execution records.
+One console process owns a workspace at a time. The web console uses the core
+Python dependencies; Node, a frontend build and CDN access are not required.
+
+## Configure and launch
+
+1. Select Local, SSH workers, or Sandbox. Fill controller/GPU/runner paths and
+ service origins. Source and interpreter paths refer to the selected machine.
+ The controller source directory and Python executable select the checkout and
+ environment used by the launched CLI.
+2. Provide existing checkpoint/reference directories, a pinned task catalog,
+ harness profile and real tokenizer/template fingerprints. The
+ [configuration CLI guide](configuration-wizard.md) describes preparing these
+ inputs. The console never downloads or generates model inputs.
+3. Choose GPU layout, tensor parallelism, rollout batches and sampling settings.
+ Allocation defaults are configuration values, not a model memory-fit estimate.
+4. Review the generated YAML and validation. **Check local files** additionally
+ checks references on the controller. Deferred GPU, connectivity, model and
+ Sandbox checks remain explicit. Save the bundle under a new configuration name.
+5. Run **Dry-run** to build the actual CLI launch plan. **Doctor** performs live
+ checks after confirmation. **Start training** requires a separate confirmation
+ and invokes the existing `harborrl train` flow, including its preflight.
+
+Configuration names are limited to letters, digits, underscores and hyphens.
+Saved configurations are not overwritten. Reopen one to launch it again, or edit
+and save under a new name. The advanced answers editor and YAML/JSON import use
+the existing `answers_version: 1` contract. Download an individual generated file
+or export answers from the editor. Advanced remote Sandbox runners can be
+specified through imported answers; the form preset defaults to a local runner.
+
+H-cluster deployments remain the optional
+[H practice](../practices/h_cluster/README.md). Prepare its Sandbox configuration
+separately and reference the path. H project/environment identifiers and relay
+settings are not part of the core web form.
+
+Windows supports configuration and dry-run. Live training/doctor controls require
+the console to run on a POSIX controller, using the same environment and checkout
+that would normally run the training CLI. Deployment readiness still depends on
+the project's supported Linux training environment.
+
+## Runs and shutdown
+
+Only one execution is active per workspace, including doctor and dry-run. This
+prevents duplicate launches within this console; it is not a cluster-wide GPU
+scheduler. Other CLI processes or different workspaces can still use the same
+resources, and operators must coordinate those allocations.
+
+Each job keeps immutable input files and a durable `job.json`/`console.log` under
+`WORKSPACE/jobs/JOB_ID`. Execution appends `web/JOB_ID` to the configured shared
+run root and updates both config files in its snapshot. The original saved bundle
+is unchanged. The existing runtime then creates its normal `training/...` run
+directory there. This isolates artifacts while retaining the configured shared
+filesystem. Run logs show the actual snapshot and output paths.
+
+The Runs view polls status and the last 64 KiB of output. **Controller** shows the
+CLI output and errors. **GPU training** reads the separate `train-stdout.log` when
+the service deployment creates one. Embedded training generally streams into
+the controller log. Full logs remain on disk; the browser download contains the
+currently displayed tail. This initial release does not aggregate WandB curves
+or claim remote/GPU progress from a quiet controller log.
+
+**Stop** sends an interrupt through the existing CLI lifecycle. Local training
+forwards it to the training process group; service mode uses the existing SSH
+target cancellation. `stopping` means cleanup is pending, not that resources are
+already free. Closing the browser does not stop a job. Ctrl+C or SIGTERM in the
+server terminal requests cleanup; shutdown waits up to 30 seconds. A timeout
+records an unknown state without force-killing arbitrary remote processes.
+
+After a crash or interrupted shutdown, previously running jobs appear as
+`unknown`. New launches are blocked. Inspect the target processes, shared run
+records and Sandbox cleanup evidence, then use **Confirm cleanup** to acknowledge
+that external cleanup is complete. This action records operator confirmation; it
+does not kill a stored PID or automatically relaunch a job. The server never
+signals a PID recovered from an old session.
+
+## Remote controller
+
+On the controller, from the intended checkout/environment:
+
+```bash
+python -m harborrl.cli web --workspace /srv/shared/web-console --port 8765 --no-browser
+```
+
+On the workstation, create a local SSH forward:
+
+```bash
+ssh -N -L 127.0.0.1:8765:127.0.0.1:8765 your-controller-alias
+```
+
+Open the URL printed by the controller in the workstation browser. If the local
+forward uses a different port, replace only the URL port. Leave the `#token=...`
+fragment intact. If either port is occupied, choose an unused port; omitting
+`--port` selects one automatically and prints it.
+
+The listener is deliberately fixed to `127.0.0.1`. A random per-session token
+protects API reads and writes; the browser moves the URL fragment into
+sessionStorage and removes it from the address bar. State-changing requests also
+require a same-origin JSON request. Responses use CSP, frame blocking and
+no-store headers, and assets are packaged locally. Keep the token private: this
+is a local single-user console with the controller user's filesystem and training
+privileges. It is not a public multi-user service or an Internet-facing gateway.
+Do not expose it through a public reverse proxy.
diff --git a/examples/native/catalog.jsonl b/examples/native/catalog.jsonl
new file mode 100644
index 000000000..eba16a31c
--- /dev/null
+++ b/examples/native/catalog.jsonl
@@ -0,0 +1 @@
+{"id": "hello_world", "path": "tasks/hello_world", "revision": "v0.1-example", "reward_profile": {"key": "reward", "offset": 0.0, "raw_range": [0.0, 1.0], "scale": 1.0}, "task_digest": "905f496d2852cca5b1e86e9de72395b41c291d2b8f6a80807e0ed24ca19541b3"}
diff --git a/examples/native/deployment-embedded.yaml b/examples/native/deployment-embedded.yaml
new file mode 100644
index 000000000..12c5cef81
--- /dev/null
+++ b/examples/native/deployment-embedded.yaml
@@ -0,0 +1,45 @@
+# Embedded (same-machine) deployment: identical to the legacy pipeline.
+# Use this file explicitly, or simply omit --deployment to get the same
+# embedded-local topology built from the training config.
+schema_version: 1
+
+manager:
+ id: embedded-local
+ mode: embedded
+ bind: 127.0.0.1
+ control_port: 49200
+ control_url: http://127.0.0.1:49200
+ gateway_port: 49184
+ gateway_url: http://127.0.0.1:49184
+ run_token_file: /run/secrets/harborrl/run-token
+ source_dir: /opt/harborrl/source
+ controller_python: /opt/harborrl/controller/bin/python
+
+gpu_target:
+ id: local
+ transport: local
+ ssh: null
+ source_dir: /opt/harborrl/source
+ train_python: /opt/harborrl/training/bin/python
+ environment_file: null
+ expected_gpus: 8
+
+storage:
+ mode: shared_fs
+ run_root: /mnt/shared/harborrl/runs
+ manager_writable: true
+ gpu_writable: true
+ worker_writable: true
+
+rollout:
+ # The embedded provider keeps worker discovery in the training config.
+ provider: embedded
+ workers: []
+
+connectivity:
+ command_timeout_s: 30
+ ssh_timeout_s: 15
+ http_timeout_s: 10
+ gpu_to_controller: true
+ worker_to_gateway: true
+ manager_to_gpu_inference: true
diff --git a/examples/native/deployment-service.yaml b/examples/native/deployment-service.yaml
new file mode 100644
index 000000000..b3584f5f9
--- /dev/null
+++ b/examples/native/deployment-service.yaml
@@ -0,0 +1,43 @@
+# Replace example hosts and paths; no site-specific addresses are required.
+schema_version: 1
+manager:
+ id: manager
+ mode: service
+ bind: 0.0.0.0
+ control_port: 49200
+ control_url: http://manager.example:49200
+ gateway_port: 49184
+ gateway_url: http://manager.example:49184
+ run_token_file: /srv/harborrl/site/run-token
+ source_dir: /srv/harborrl/repo
+ controller_python: /opt/harborrl/controller/bin/python
+gpu_target:
+ id: trainer
+ transport: ssh
+ ssh: {host: training-host}
+ source_dir: /srv/harborrl/repo
+ train_python: /opt/harborrl/training/bin/python
+ environment_file: /srv/harborrl/site/gpu-env.json
+ expected_gpus: 8
+storage:
+ mode: shared_fs
+ run_root: /srv/harborrl/runs
+ manager_writable: true
+ gpu_writable: true
+ worker_writable: true
+rollout:
+ provider: ssh_docker
+ workers:
+ - id: worker-a
+ ssh: {host: rollout-host}
+ source_dir: /srv/harborrl/repo
+ runner_python: /opt/harborrl/runner/bin/python
+ docker: true
+ task_roots: [/srv/harborrl/tasks]
+connectivity:
+ command_timeout_s: 30
+ ssh_timeout_s: 15
+ http_timeout_s: 10
+ gpu_to_controller: true
+ worker_to_gateway: true
+ manager_to_gpu_inference: true
diff --git a/examples/native/sandbox.yaml b/examples/native/sandbox.yaml
new file mode 100644
index 000000000..4ab3788a5
--- /dev/null
+++ b/examples/native/sandbox.yaml
@@ -0,0 +1,10 @@
+# Standard OpenSandbox; install harborrl[sandbox] in a Python 3.12+ runner.
+domain: https://sandbox.example
+credential_file: /run/secrets/harborrl/sandbox-key
+max_live_instances: 2
+startup_concurrency: 1
+bindings:
+ - image: registry.example/project/task:locked-version
+ max_live_instances: 2
+# Omit installation to use normal Harbor agent setup. Offline deployments
+# may supply mode, version, binary and sha256; see docs/sandbox-rollout.md.
diff --git a/examples/options/embedded-local.answers.yaml b/examples/options/embedded-local.answers.yaml
new file mode 100644
index 000000000..7a4bc5db1
--- /dev/null
+++ b/examples/options/embedded-local.answers.yaml
@@ -0,0 +1,30 @@
+# Replace every example path and REPLACE_* value. No model fit is implied.
+answers_version: 1
+preset: embedded-local
+training:
+ tasks:
+ catalog: /srv/harborrl/inputs/tasks.jsonl
+ harness:
+ profile: /srv/harborrl/inputs/claude-code.json
+ harbor:
+ python: /opt/harbor/bin/python
+ model:
+ checkpoint: /srv/models/qwen
+ reference: /srv/models/qwen-reference
+ args_file: qwen3-8B
+ gateway:
+ tokenizer_digest: REPLACE_WITH_ACTUAL_TOKENIZER_SHA256
+ template_digest: REPLACE_WITH_ACTUAL_TEMPLATE_SHA256
+ deployment:
+ layout: colocate
+ num_gpus: 1
+ sampling:
+ group_size: 2
+ groups_per_batch: 1
+deployment:
+ manager:
+ source_dir: /srv/harborrl/source
+ controller_python: /opt/controller/bin/python
+ gpu_target:
+ source_dir: /srv/harborrl/source
+ train_python: /opt/training/bin/python
diff --git a/examples/options/service-sandbox.answers.yaml b/examples/options/service-sandbox.answers.yaml
new file mode 100644
index 000000000..a55de6d2f
--- /dev/null
+++ b/examples/options/service-sandbox.answers.yaml
@@ -0,0 +1,34 @@
+# Prepare a standard or practice-specific Sandbox config separately.
+answers_version: 1
+preset: service-sandbox-local-runner
+sandbox_config: /srv/shared/site/sandbox.yaml
+training:
+ tasks:
+ catalog: /srv/shared/inputs/tasks.jsonl
+ harness:
+ profile: /srv/shared/inputs/claude-code.json
+ harbor:
+ python: /opt/sandbox-runner/bin/python
+ model:
+ checkpoint: /srv/models/qwen
+ reference: /srv/models/qwen-reference
+ args_file: qwen3-8B
+ gateway:
+ tokenizer_digest: REPLACE_WITH_ACTUAL_TOKENIZER_SHA256
+ template_digest: REPLACE_WITH_ACTUAL_TEMPLATE_SHA256
+ deployment:
+ layout: colocate
+ num_gpus: 1
+deployment:
+ manager:
+ source_dir: /srv/shared/harborrl
+ controller_python: /opt/controller/bin/python
+ control_url: http://controller.example:49200
+ gateway_url: http://controller.example:49184
+ gpu_target:
+ ssh:
+ host: gpu-training
+ source_dir: /srv/shared/harborrl
+ train_python: /opt/training/bin/python
+ storage:
+ run_root: /srv/shared/runs
diff --git a/examples/options/service-ssh.answers.yaml b/examples/options/service-ssh.answers.yaml
new file mode 100644
index 000000000..7a5851da4
--- /dev/null
+++ b/examples/options/service-ssh.answers.yaml
@@ -0,0 +1,44 @@
+# All paths refer to the named role's machine. Replace before use.
+answers_version: 1
+preset: service-ssh
+training:
+ tasks:
+ catalog: /srv/shared/inputs/tasks.jsonl
+ harness:
+ profile: /srv/shared/inputs/claude-code.json
+ model:
+ checkpoint: /srv/models/qwen
+ reference: /srv/models/qwen-reference
+ args_file: qwen3-8B
+ gateway:
+ tokenizer_digest: REPLACE_WITH_ACTUAL_TOKENIZER_SHA256
+ template_digest: REPLACE_WITH_ACTUAL_TEMPLATE_SHA256
+ deployment:
+ layout: split
+ num_gpus: 8
+ actor_gpus: 4
+ rollout_gpus: 4
+ actor_tensor_parallel_size: 4
+ rollout_gpus_per_engine: 4
+deployment:
+ manager:
+ source_dir: /srv/shared/harborrl
+ controller_python: /opt/controller/bin/python
+ control_url: http://controller.example:49200
+ gateway_url: http://controller.example:49184
+ gpu_target:
+ ssh:
+ host: gpu-training
+ source_dir: /srv/shared/harborrl
+ train_python: /opt/training/bin/python
+ storage:
+ run_root: /srv/shared/runs
+ rollout:
+ workers:
+ - id: runner-0
+ ssh:
+ host: rollout-worker
+ source_dir: /srv/shared/harborrl
+ runner_python: /opt/harbor/bin/python
+ docker: true
+ task_roots: [/srv/shared/tasks]
diff --git a/harborrl/algorithms/__init__.py b/harborrl/algorithms/__init__.py
new file mode 100644
index 000000000..61b32100b
--- /dev/null
+++ b/harborrl/algorithms/__init__.py
@@ -0,0 +1 @@
+"""Training algorithm adapters independent of backend hook plumbing."""
diff --git a/harborrl/algorithms/grpo.py b/harborrl/algorithms/grpo.py
new file mode 100644
index 000000000..34e98d7dc
--- /dev/null
+++ b/harborrl/algorithms/grpo.py
@@ -0,0 +1,18 @@
+"""GRPO semantics backed by the existing CPU-verified implementations."""
+from __future__ import annotations
+
+from harborrl.backends.slime_v032.advantage import compute_advantages_and_returns
+from harborrl.backends.slime_v032.loss import clipped_loss, loss_function
+
+
+class GRPOAlgorithmAdapter:
+ algorithm_id = "grpo"
+
+ def compute_advantages_and_returns(self, args, rollout_data):
+ return compute_advantages_and_returns(args, rollout_data)
+
+ def clipped_loss(self, *args, **kwargs):
+ return clipped_loss(*args, **kwargs)
+
+ def backend_loss_function(self, *args, **kwargs):
+ return loss_function(*args, **kwargs)
diff --git a/harborrl/backends/slime_v032/config.py b/harborrl/backends/slime_v032/config.py
new file mode 100644
index 000000000..cc52b864d
--- /dev/null
+++ b/harborrl/backends/slime_v032/config.py
@@ -0,0 +1,125 @@
+"""Schema-2 to official Slime launch mapping; no live probes."""
+import json
+import os
+import sys
+
+CONFIG_ENV = {
+ "HF_CKPT": ("model", "checkpoint"),
+ "REF_LOAD": ("model", "reference"),
+ "MODEL_ARGS_FILE": ("model", "args_file"),
+ "MODEL_TAG": ("model", "args_file"),
+ "NUM_ROLLOUT": ("training", "num_rollout"),
+ "SAVE_INTERVAL": ("training", "save_interval"),
+ "LR": ("training", "learning_rate"),
+ "ROLLOUT_BATCH_SIZE": ("sampling", "groups_per_batch"),
+ "N_SAMPLES": ("sampling", "group_size"),
+ "ROLLOUT_MAX_RESPONSE_LEN": ("sampling", "max_tokens"),
+ "ROLLOUT_MAX_CONTEXT_LEN": ("sampling", "max_context"),
+ "NUM_GPUS": ("deployment", "num_gpus"),
+ "ACTOR_GPUS": ("deployment", "actor_gpus"),
+ "ROLLOUT_GPUS": ("deployment", "rollout_gpus"),
+ "TP_SIZE": ("deployment", "actor_tensor_parallel_size"),
+ "ROLLOUT_NUM_GPUS_PER_ENGINE": ("deployment", "rollout_gpus_per_engine"),
+ "HARBORRL_GPU_LAYOUT": ("deployment", "layout"),
+ "HARBORRL_NATIVE_RUNNER_PYTHON": ("harbor", "python"),
+ "HARBORRL_NATIVE_PROFILE": ("harness", "profile"),
+ "HARBORRL_NATIVE_CATALOG": ("tasks", "catalog"),
+ "HARBORRL_NATIVE_GATEWAY_HOST": ("gateway", "host"),
+ "HARBORRL_NATIVE_GATEWAY_PORT": ("gateway", "port"),
+ "HARBORRL_NATIVE_GATEWAY_URL": ("gateway", "advertised_url"),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": ("gateway", "tokenizer_digest"),
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": ("gateway", "template_digest"),
+ "HARBORRL_NATIVE_MAX_ATTEMPTS": ("sampling", "max_attempts"),
+ "RUNS_ROOT": ("output", "root"),
+}
+
+
+def launch_plan(config):
+ from harborrl.backends.slime_v032.versions import runtime_contract_id
+ from harborrl.config import ROOT
+ from harborrl.config.native import validate, validate_files
+ from harborrl.math_registry import select_math
+ from harborrl.tasks.catalog import catalog
+ validate(config, ROOT / "config.yaml")
+ validate_files(config)
+ selection = select_math(config)
+ entries = catalog(config)
+ runtime = config['harbor'].get('environment', {'runtime': 'docker'})
+ sandbox = None
+ if runtime['runtime'] == 'sandbox':
+ from harborrl.rollout.harbor_job.sandbox_config import (
+ load_sandbox,
+ task_binding,
+ )
+ from harborrl.trajectories.native import read_json
+ sandbox = load_sandbox(runtime['config'])
+ profile = read_json(config['harness']['profile'])
+ if sandbox['installation'] and sandbox['installation']['version'] != profile['cli_version']:
+ raise ValueError('sandbox installation version differs from harness profile')
+ budget = sum(profile[k] for k in ('agent_timeout_sec', 'setup_timeout_sec', 'verifier_timeout_sec'))
+ if sandbox['lifecycle_minutes'] * 60 <= budget + sandbox['create_timeout_s'] + 2 * sandbox['cleanup_timeout_s']:
+ raise ValueError('sandbox lifecycle must cover setup, agent, verifier and cleanup budgets')
+ for entry in entries:
+ task_binding(sandbox, entry['path'])
+ c = config
+ worker_overrides = os.environ.get('HARBORRL_NATIVE_TASK_WORKER_OVERRIDES', '{}')
+ try:
+ overrides = json.loads(worker_overrides)
+ valid_workers = set(c['harbor']['workers'])
+ if (not isinstance(overrides, dict) or any(not isinstance(key, str) or not isinstance(value, str) or value not in valid_workers
+ for key, value in overrides.items())):
+ raise ValueError
+ except (ValueError, json.JSONDecodeError) as exc:
+ raise ValueError('invalid HARBORRL_NATIVE_TASK_WORKER_OVERRIDES') from exc
+ environment = {
+ **{name: c[section][field] for name, (section, field) in CONFIG_ENV.items()},
+ 'DATASET': selection.task.id, 'ALGO': selection.algorithm.id,
+ 'HARNESS_OPTION': f"native-{selection.harness.id.replace('_', '-')}",
+ 'TRAIN_PYTHON': sys.executable,
+ 'HARBORRL_STRUCTURED_LAUNCH': '1', 'HARBORRL_VERIFY_POLICY_POOL': '1',
+ 'HARBORRL_SKIP_GLOBAL_CLEANUP': '1', 'SLIME_RAY_PLACEMENT_GPU_PROBE': '1',
+ 'PYTORCH_CUDA_ALLOC_CONF': ('max_split_size_mb:2048' if c['deployment']['layout'] == 'colocate'
+ else 'max_split_size_mb:2048,expandable_segments:True'),
+ 'HARBORRL_NATIVE_ROLLOUT': '1',
+ 'HARBORRL_NATIVE_SLIME_CONTRACT': runtime_contract_id(c['training']['backend_contract']),
+ 'HARBORRL_NATIVE_RUNNER_WORKERS': json.dumps(c['harbor']['workers']),
+ 'HARBORRL_NATIVE_TASK_WORKER_OVERRIDES': json.dumps(overrides, sort_keys=True),
+ 'HARBORRL_NATIVE_AUDITED_LOGPROBS': str(int(c['gateway']['audited_raw_logprobs'])),
+ 'HARBORRL_NATIVE_DRAIN_TIMEOUT': '30',
+ 'WANDB_ENABLE': '0',
+ 'HARBORRL_CONSISTENCY': json.dumps(c['consistency'], sort_keys=True),
+ 'HARBORRL_CONSISTENCY_MODE': c['consistency']['mode'],
+ }
+ environment['HARBORRL_NATIVE_RUNNER_TRANSPORT'] = c['harbor'].get('runner_transport', 'ssh')
+ if sandbox is not None:
+ environment['HARBORRL_NATIVE_SANDBOX'] = json.dumps(sandbox, sort_keys=True)
+ return {'config': config, 'math': selection.asdict(),
+ 'command': [sys.executable, '-m', 'harborrl.platform.native_train'],
+ 'environment': {key: str(value) for key, value in environment.items()},
+ 'training_command': ['bash', str(ROOT / 'harborrl/platform/slime_train.sh')],
+ 'required_services': ['GPU Ray / SGLang / Messages Gateway',
+ 'Sandbox / Harbor runner' if sandbox else 'external CPU Harbor Docker workers'],
+ 'launcher': f"native {selection.harness.display_name} → Messages Gateway → "
+ f"{selection.task.display_name} verifier → "
+ f"{selection.algorithm.display_name}",
+ 'layout': {'mode': config['deployment']['layout'], 'offload': config['deployment']['layout']=='colocate'}}
+
+
+def field_usage():
+ """Account for every schema-2 field, including non-environment consumers."""
+ usage = {}
+ from harborrl.consistency.config import DEFAULTS
+ usage.update({f'consistency.{key}': ['environment:HARBORRL_CONSISTENCY'] for key in DEFAULTS})
+ for name, (section, field) in CONFIG_ENV.items():
+ usage.setdefault(f"{section}.{field}", []).append(f"environment:{name}")
+ usage.update({
+ "execution.backend": ["schema:harbor_job"],
+ "harness.name": ["schema:claude_code", "runner:Claude Code"],
+ "harbor.version": ["preflight:worker runtime version"],
+ "training.backend_contract": ["environment:HARBORRL_NATIVE_SLIME_CONTRACT", "preflight:backend hooks"],
+ "harbor.workers": ["environment:HARBORRL_NATIVE_RUNNER_WORKERS", "preflight:worker probe"],
+ "harbor.runner_transport": ["environment:HARBORRL_NATIVE_RUNNER_TRANSPORT"],
+ "harbor.environment": ["environment:HARBORRL_NATIVE_SANDBOX", "runner:environment"],
+ "gateway.audited_raw_logprobs": ["environment:HARBORRL_NATIVE_AUDITED_LOGPROBS"],
+ })
+ return usage
diff --git a/harborrl/backends/slime_v032/converter.py b/harborrl/backends/slime_v032/converter.py
index 6098332b3..075a121ae 100644
--- a/harborrl/backends/slime_v032/converter.py
+++ b/harborrl/backends/slime_v032/converter.py
@@ -131,6 +131,8 @@ def convert_samples_to_train_data(args, samples):
data["loss_masks"].append(response_mask)
data["rollout_log_probs"].append(list(span.old_logprobs))
data["rollout_mask_sums"].append(unit.token_count)
+ from harborrl.consistency.slime import record_turn
+ record_turn(ir["turns"][turn_index], list(span.tokens), list(span.old_logprobs), response_mask)
for group in batch.groups:
weight_sum = math.fsum(
1.0 / (group.group_size * unit.token_count)
diff --git a/harborrl/backends/slime_v032/launcher.py b/harborrl/backends/slime_v032/launcher.py
index c0f5c9d0d..3450782d1 100644
--- a/harborrl/backends/slime_v032/launcher.py
+++ b/harborrl/backends/slime_v032/launcher.py
@@ -4,40 +4,37 @@
import sys
from harborrl.backends.slime_v032.versions import BACKEND_CONTRACT
+from harborrl.math_registry import MATH_REGISTRY
-HOOK_PATHS = {
- "rollout": "harborrl.backends.slime_v032.rollout.generate_rollout",
- "converter": "harborrl.backends.slime_v032.converter.convert_samples_to_train_data",
- "advantage": "harborrl.backends.slime_v032.advantage.compute_advantages_and_returns",
- "loss": "harborrl.backends.slime_v032.loss.loss_function",
- "postprocess": "harborrl.backends.slime_v032.postprocess.rollout_data_postprocess",
-}
-
-def hook_arguments(contract=BACKEND_CONTRACT):
+def _hooks(contract=BACKEND_CONTRACT):
if str(contract) != BACKEND_CONTRACT:
raise ValueError(f"unsupported backend contract: {contract!r}")
+ return MATH_REGISTRY.algorithm_for_contract(str(contract)).backend_hooks
+
+
+def hook_arguments(contract=BACKEND_CONTRACT):
+ hooks = _hooks(contract)
return [
- "--rollout-function-path", HOOK_PATHS["rollout"],
- "--custom-convert-samples-to-train-data-path", HOOK_PATHS["converter"],
- "--custom-advantage-function-path", HOOK_PATHS["advantage"],
+ "--rollout-function-path", hooks["rollout"],
+ "--custom-convert-samples-to-train-data-path", hooks["converter"],
+ "--custom-advantage-function-path", hooks["advantage"],
"--loss-type", "custom_loss",
- "--custom-loss-function-path", HOOK_PATHS["loss"],
- "--rollout-data-postprocess-path", HOOK_PATHS["postprocess"],
+ "--custom-loss-function-path", hooks["loss"],
+ "--rollout-data-postprocess-path", hooks["postprocess"],
]
def validate_hook_assignment(assignment, *, contract=BACKEND_CONTRACT):
- if str(contract) != BACKEND_CONTRACT:
- raise ValueError(f"unsupported backend contract: {contract!r}")
- observed = {key: value for key, value in assignment.items() if key in HOOK_PATHS}
- if observed != HOOK_PATHS:
+ hooks = _hooks(contract)
+ observed = {key: value for key, value in assignment.items() if key in hooks}
+ if observed != hooks:
raise ValueError(f"Slime hook assignment must exactly match {BACKEND_CONTRACT}")
return observed
def hook_modules():
- return {name: path.rsplit(".", 1)[0] for name, path in HOOK_PATHS.items()}
+ return {name: path.rsplit(".", 1)[0] for name, path in _hooks().items()}
def dry_run_command(entrypoint, base_args=(), *, contract=BACKEND_CONTRACT, python=sys.executable):
diff --git a/harborrl/backends/slime_v032/loss.py b/harborrl/backends/slime_v032/loss.py
index 72f386168..502c146eb 100644
--- a/harborrl/backends/slime_v032/loss.py
+++ b/harborrl/backends/slime_v032/loss.py
@@ -104,5 +104,12 @@ def loss_function(args, batch, logits, unused_reducer):
"native_token_count": loss_masks.sum().detach(),
"native_group_count": weight_sum.detach().round(),
}
+ from harborrl.consistency.config import environment
+ if environment()["mode"] != "off":
+ p0 = torch.cat(batch["log_probs"]).detach()
+ current = torch.cat(output["log_probs"]).detach()
+ metrics["consistency_backend_abs_sum"] = ((p0 - old_logprobs).abs() * loss_masks).sum()
+ metrics["consistency_update_abs_sum"] = ((current - p0).abs() * loss_masks).sum()
+ metrics["consistency_valid_tokens"] = loss_masks.sum().detach()
return loss * LOSS_SCALE_CORRECTION, metrics
diff --git a/harborrl/backends/slime_v032/postprocess.py b/harborrl/backends/slime_v032/postprocess.py
index d778412bc..7d4c89bb3 100644
--- a/harborrl/backends/slime_v032/postprocess.py
+++ b/harborrl/backends/slime_v032/postprocess.py
@@ -3,8 +3,6 @@
import math
-from harborrl.trajectories.native import finite
-
REQUIRED_FIELDS = (
"tokens",
"response_lengths",
@@ -34,7 +32,7 @@ def _integer_scalar(value):
return int(number) if math.isfinite(number) and number.is_integer() else None
-def rollout_data_postprocess(args, rollout_id, rollout_data):
+def _validate_rollout_data(args, rollout_id, rollout_data):
"""Slime ``--rollout-data-postprocess-path`` hook; fail closed on drift."""
missing = [field for field in REQUIRED_FIELDS if field not in rollout_data]
if missing:
@@ -51,8 +49,11 @@ def rollout_data_postprocess(args, rollout_id, rollout_data):
group_size = int(getattr(args, "n_samples_per_prompt", 0))
if group_size < 2:
raise ValueError("native training requires a complete group")
- local_weight_sum = 0.0
+ group_weights = {}
for index in range(count):
+ group_id = _integer_scalar(rollout_data["rollout_ids"][index])
+ if group_id is None or group_id < 0:
+ raise ValueError("invalid native rollout id")
response_length = _integer_scalar(rollout_data["response_lengths"][index])
mask = rollout_data["loss_masks"][index]
logprobs = rollout_data["rollout_log_probs"][index]
@@ -71,8 +72,22 @@ def rollout_data_postprocess(args, rollout_id, rollout_data):
raise ValueError("invalid native rollout mask sum")
if truncated is None or truncated not in (0, 1):
raise ValueError("invalid native truncated flag")
- local_weight_sum += sum(
- 1.0 / (group_size * mask_sum) for flag in mask if flag
+ group_weights.setdefault(group_id, []).append(
+ sum(1 for flag in mask if flag) / (group_size * mask_sum)
)
- if not finite(local_weight_sum) or local_weight_sum > 1.0 + 1e-9:
- raise ValueError(f"native local weights exceed the one-per-group budget: {local_weight_sum}")
+ # Actor shards may contain partial groups; each group has its own unit budget.
+ for group_id, weights in group_weights.items():
+ local_weight_sum = math.fsum(weights)
+ if not math.isfinite(local_weight_sum) or local_weight_sum > 1.0 + 1e-9:
+ raise ValueError(
+ f"native local weights exceed the one-per-group budget for rollout id "
+ f"{group_id}: {local_weight_sum}"
+ )
+
+
+def rollout_data_postprocess(args, rollout_id, rollout_data):
+ from harborrl.consistency.config import environment
+ if environment()["mode"] == "off":
+ return _validate_rollout_data(args, rollout_id, rollout_data)
+ from harborrl.consistency.slime import check_training_batch
+ return check_training_batch(args, rollout_id, rollout_data, _validate_rollout_data)
diff --git a/harborrl/backends/slime_v032/rollout.py b/harborrl/backends/slime_v032/rollout.py
index c232921c2..44c36e2c8 100644
--- a/harborrl/backends/slime_v032/rollout.py
+++ b/harborrl/backends/slime_v032/rollout.py
@@ -1,11 +1,28 @@
-"""Slime v0.3.2 rollout function hook for complete Native Harbor groups."""
+"""Slime v0.3.2 rollout function hook for complete Native Harbor groups.
+
+This module is the training-side adapter: it maps Slime args and Sample
+objects onto the framework-free rollout core. When the manager runs the
+RolloutService (P3), groups are requested over HTTP instead of executing in
+this process; conversion to Slime samples is identical either way.
+"""
from __future__ import annotations
import asyncio
import math
+import os
+from pathlib import Path
from harborrl.backends.slime_v032.versions import lock_policy_version
from harborrl.export.native import export_training_batch
+from harborrl.rollout.runtime import (
+ RolloutRuntimeConfig,
+ close_runtime,
+ current_policy_version,
+ digest,
+ run_group,
+ runtime,
+ task_entry,
+)
def sampling_params(args):
@@ -21,6 +38,30 @@ def sampling_params(args):
}
+def runtime_config_from_args(args):
+ """Map Slime runtime args onto the extracted rollout core config."""
+ endpoint = f"http://{args.sglang_router_ip}:{args.sglang_router_port}"
+ engine_count = int(args.rollout_num_gpus) // int(args.rollout_num_gpus_per_engine)
+ limit = max(1, int(args.sglang_server_concurrency) * engine_count)
+ return RolloutRuntimeConfig.from_environment(
+ os.environ, endpoint=endpoint, concurrency_limit=limit,
+ group_size=int(args.n_samples_per_prompt))
+
+
+def slime_tokenizer_loader(args):
+ """The single Slime dependency at the rollout boundary: the tokenizer."""
+ from slime.utils.processing_utils import load_tokenizer
+
+ return lambda: load_tokenizer(args.hf_checkpoint, trust_remote_code=True)
+
+
+def group_index_of(group):
+ indexes = {getattr(sample, "group_index", None) for sample in group}
+ if len(indexes) != 1 or type(next(iter(indexes), None)) is not int:
+ raise ValueError("Slime must provide one group identity before native generation")
+ return indexes.pop()
+
+
def assert_locked_policy_versions(ir_groups, policy_version):
"""Fail closed if any generation escaped the locked weight version."""
for group in ir_groups:
@@ -32,11 +73,128 @@ def assert_locked_policy_versions(ir_groups, policy_version):
raise ValueError("native rollout observed a stale policy version")
+async def generate_group(args, group, sampling_params_, rollout_id, evaluation=False):
+ """Run one complete native group; evaluation is intentionally unsupported."""
+ from harborrl.math_registry import NATIVE_TASK_PROVIDER
+
+ if evaluation:
+ raise ValueError("native training rollout cannot be reused as evaluation")
+ if len(group) != args.n_samples_per_prompt:
+ raise ValueError("native rollout group is incomplete before launch")
+ entries = [task_entry(sample) for sample in group]
+ if any(entry != entries[0] for entry in entries[1:]):
+ raise ValueError("native rollout group mixes task locks")
+ entry = entries[0]
+ report = NATIVE_TASK_PROVIDER.adapter.inspect(
+ entry["path"], expected_digest=entry["task_digest"]
+ )
+ if report["status"] != "NEEDS_PROBE":
+ raise ValueError(f"native task preflight failed: {report['reasons']}")
+ stop = list(sampling_params_.get("stop") or [])
+ sampling_digest = digest({
+ "temperature": sampling_params_.get("temperature"),
+ "top_p": sampling_params_.get("top_p"),
+ "top_k": sampling_params_.get("top_k"),
+ "max_new_tokens": sampling_params_.get("max_new_tokens"),
+ "stop": stop,
+ })
+ config = runtime_config_from_args(args)
+ runtime_ = await runtime(config, tokenizer_loader=slime_tokenizer_loader(args))
+ policy_version = current_policy_version(runtime_)
+ trajectories = await run_group(
+ config, runtime_, entry=entry, group_index=group_index_of(group),
+ rollout_id=rollout_id, sampling_digest=sampling_digest,
+ policy_version=policy_version,
+ )
+ return bind_group_samples(group, trajectories)
+
+
+def bind_group_samples(group, trajectories):
+ """Attach trajectory evidence to Slime samples; order defines the slots."""
+ output = []
+ for sample, ir in zip(group, trajectories):
+ sample.metadata["native_ir"] = ir
+ sample.response = "native Harbor trial"
+ sample.reward = {"score": ir["evaluation"]["training_reward"]}
+ sample.status = sample.Status.COMPLETED
+ try:
+ sample.policy_version = int(ir["identity"]["policy_version"])
+ except ValueError as exc:
+ raise ValueError("native policy version must be an integer in Slime") from exc
+ output.append(sample)
+ return output
+
+
+async def run_service_groups(args, groups, rollout_id):
+ """Request complete groups from the manager-side RolloutService (P3)."""
+ from harborrl.backends.slime_v032.rollout_service_client import (
+ RolloutServiceClient,
+ fetch_pool_states,
+ load_group_result,
+ service_group_request,
+ )
+
+ token_file = os.environ.get('HARBORRL_ROLLOUT_SERVICE_TOKEN_FILE')
+ token = Path(token_file).read_text().strip() if token_file else os.environ.get('HARBORRL_ROLLOUT_SERVICE_TOKEN', '')
+ client = RolloutServiceClient(
+ os.environ["HARBORRL_ROLLOUT_SERVICE_URL"], os.environ["RUN_ID"],
+ token)
+ router = f"http://{args.sglang_router_ip}:{args.sglang_router_port}"
+ # Policy lifecycle (§7): the GPU job publishes its serving endpoint, then
+ # the manager admits the version the complete pool actually serves. The
+ # manager never opens a version some engine has not reached yet.
+ await asyncio.to_thread(client.announce_serving, router)
+ states = await asyncio.to_thread(fetch_pool_states, router)
+ from harborrl.consistency.config import environment
+ from harborrl.consistency.sglang import admit_pool
+ if environment()['mode'] != 'off':
+ await asyncio.to_thread(admit_pool, states, os.environ["RUN_DIR"])
+ await asyncio.to_thread(client.activate_policy, states, states[0]["weight_version"])
+ # The manager's activation publishes the locked policy-pool record into
+ # the shared run directory; lock the same version before requesting.
+ policy_version = lock_policy_version(args)
+ params = sampling_params(args)
+ async def request_group(group):
+ request = service_group_request(group, params, rollout_id, policy_version)
+ payload = await asyncio.to_thread(client.submit_group, request)
+ # The HTTP layer returns a status summary; completed work carries the
+ # GroupResult itself.
+ result = payload.get("result") if isinstance(payload, dict) else None
+ if not isinstance(result, dict):
+ from harborrl.backends.slime_v032.rollout_service_client import (
+ ServiceRolloutError,
+ )
+ raise ServiceRolloutError(
+ f"group {request['group_id']} is {payload.get('status')!r}: "
+ f"{payload.get('error', '')}")
+ irs = load_group_result(client.run_id, request, result, allow_synthetic=False)
+ bind_group_samples(group, irs)
+ return irs
+
+ pending = [asyncio.create_task(request_group(group)) for group in groups]
+ try:
+ results = await asyncio.gather(*pending, return_exceptions=True)
+ # Retain independent groups' evidence, but never export a partial batch.
+ for result in results:
+ if isinstance(result, BaseException):
+ raise result
+ return results
+ except BaseException:
+ # HTTP workers keep running until the manager acknowledges cancellation.
+ try:
+ await asyncio.to_thread(client.cancel_run)
+ finally:
+ await asyncio.gather(*pending, return_exceptions=True)
+ raise
+
+
async def run_native_groups(args, groups, policy_version, rollout_id):
"""Run every group concurrently and return only complete IR evidence."""
- from harborrl.rollout.native_generate import close_runtime, generate_group
-
params = sampling_params(args)
+ if os.environ.get("HARBORRL_ROLLOUT_SERVICE_URL"):
+ ir_groups = await run_service_groups(args, groups, rollout_id)
+ assert_locked_policy_versions(ir_groups, policy_version)
+ return ir_groups
try:
results = await asyncio.gather(*(
generate_group(args, group, params, rollout_id=rollout_id) for group in groups
diff --git a/harborrl/backends/slime_v032/rollout_service_client.py b/harborrl/backends/slime_v032/rollout_service_client.py
new file mode 100644
index 000000000..4fc73e492
--- /dev/null
+++ b/harborrl/backends/slime_v032/rollout_service_client.py
@@ -0,0 +1,210 @@
+"""SlimeAdapter HTTP client (P3): complete groups from the RolloutService.
+
+The GPU job never trusts a GroupResult: every manifest digest is recomputed,
+identities are checked against the request, the group must be complete with
+at least two RL_READY slots, and the policy version must be the locked one.
+After a transport timeout the client queries status; it never resubmits.
+"""
+from __future__ import annotations
+
+import json
+import time
+from pathlib import Path
+from urllib import error, parse, request
+
+from harborrl.rollout.runtime import task_entry
+from harborrl.trajectories.native import digest, read_json, require_ready
+
+SERVICE_SCHEMA = "harborrl-rollout-service-v1"
+
+
+def fetch_pool_states(endpoint, *, timeout=120, opener=None):
+ """Read the serving pool states directly from the SGLang router.
+
+ Mirrors SGLangBackend.pool_states without a tokenizer: the GPU job calls
+ this before asking the manager to admit the version it currently serves.
+ """
+ import json as json_module
+ from urllib import request as url_request
+
+ opener = opener or url_request.build_opener(url_request.ProxyHandler({}))
+ endpoint = endpoint.rstrip("/")
+
+ def get(url):
+ with opener.open(url_request.Request(url), timeout=timeout) as response:
+ return json_module.load(response)
+
+ workers = get(endpoint + "/workers").get("workers")
+ if not isinstance(workers, list) or not workers:
+ raise ServiceRolloutError("serving router returned no workers")
+ states = []
+ for worker in workers:
+ engine = worker.get("url") if isinstance(worker, dict) else None
+ if not isinstance(engine, str) or not engine.strip():
+ raise ServiceRolloutError("serving router returned an invalid worker URL")
+ engine = engine.rstrip("/")
+ version = get(engine + "/get_weight_version").get("weight_version")
+ if not isinstance(version, str) or not version.strip():
+ raise ServiceRolloutError("missing serving weight version")
+ states.append({"endpoint": engine, "healthy": True,
+ "weight_version": version})
+ versions = {state["weight_version"] for state in states}
+ if len(versions) != 1:
+ raise ServiceRolloutError(
+ f"serving pool is mid-update; retry after all engines agree: {sorted(versions)}")
+ return states
+
+
+class ServiceRolloutError(RuntimeError):
+ def __init__(self, message, *, status=None, code=None):
+ super().__init__(message)
+ self.status, self.code = status, code
+
+
+def service_group_request(group, sampling_params, rollout_id, policy_version):
+ """Build one GroupRequest from a Slime sample group."""
+ entries = [task_entry(sample) for sample in group]
+ if any(entry != entries[0] for entry in entries[1:]):
+ raise ValueError("native rollout group mixes task locks")
+ indexes = {getattr(sample, "group_index", None) for sample in group}
+ if len(indexes) != 1 or type(next(iter(indexes), None)) is not int:
+ raise ValueError("Slime must provide one group identity before native generation")
+ index = indexes.pop()
+ return {
+ "group_id": f"group-{index}",
+ "rollout_id": int(rollout_id),
+ "group_index": index,
+ "policy_version": str(policy_version),
+ "task": entries[0],
+ "sampling": {key: sampling_params.get(key) for key in
+ ("temperature", "top_p", "top_k", "max_new_tokens", "stop")},
+ "slots": [{"slot_id": str(slot)} for slot in range(len(group))],
+ }
+
+
+class RolloutServiceClient:
+ """Synchronous urllib client; safe to call from asyncio.to_thread."""
+
+ def __init__(self, base_url, run_id, token, *, timeout=120, poll_interval=1.0,
+ opener=None):
+ self.base_url = base_url.rstrip("/")
+ self.run_id = run_id
+ self.token = token
+ self.timeout = timeout
+ self.poll_interval = poll_interval
+ self.opener = opener or request.build_opener(request.ProxyHandler({}))
+
+ def _request(self, method, path, body=None):
+ url = f"{self.base_url}{path}"
+ data = json.dumps(body).encode() if body is not None else None
+ headers = {"Content-Type": "application/json"}
+ if self.token:
+ headers["Authorization"] = f"Bearer {self.token}"
+ req = request.Request(url, data=data, headers=headers, method=method)
+ try:
+ with self.opener.open(req, timeout=self.timeout) as response:
+ return json.load(response)
+ except error.HTTPError as exc:
+ try:
+ payload = json.load(exc)
+ except (ValueError, OSError):
+ payload = {"error": {"type": "transport", "message": str(exc)}}
+ error_payload = payload.get("error", payload)
+ raise ServiceRolloutError(
+ f"{method} {path}: {error_payload.get('type')}: "
+ f"{error_payload.get('message')}",
+ status=exc.code, code=error_payload.get("type")) from exc
+ except (error.URLError, OSError, ValueError) as exc:
+ raise ServiceRolloutError(f"{method} {path}: {exc}",
+ code="transport") from exc
+
+ def health(self):
+ return self._request("GET", "/healthz")
+
+ def register(self, manifest):
+ return self._request("POST", f"/runs/{parse.quote(self.run_id)}/register",
+ {"manifest": manifest})
+
+ def submit_group(self, group_request, *, max_attempts=None):
+ """Submit idempotently; on timeout poll status, never regenerate."""
+ path = f"/runs/{parse.quote(self.run_id)}/groups"
+ try:
+ status = self._request('POST', path, group_request)
+ except ServiceRolloutError as exc:
+ if exc.code != 'transport':
+ raise
+ status = self.group_status(group_request['group_id'])
+ polls = 0
+ while status['status'] in ('queued', 'running'):
+ if max_attempts is not None and polls >= max_attempts:
+ raise ServiceRolloutError('group polling deadline reached', code='transport')
+ polls += 1
+ time.sleep(self.poll_interval)
+ status = self.group_status(group_request["group_id"])
+ return status
+
+ def group_status(self, group_id):
+ return self._request(
+ "GET", f"/runs/{parse.quote(self.run_id)}/groups/{parse.quote(group_id)}")
+
+ def cancel_group(self, group_id):
+ return self._request(
+ "POST",
+ f"/runs/{parse.quote(self.run_id)}/groups/{parse.quote(group_id)}/cancel")
+
+ def activate_policy(self, states, expected):
+ return self._request(
+ "POST", f"/runs/{parse.quote(self.run_id)}/policy/activate",
+ {"states": states, "expected": str(expected)})
+
+ def announce_serving(self, router_url):
+ return self._request(
+ "POST", f"/runs/{parse.quote(self.run_id)}/serving/announce",
+ {"router_url": router_url})
+
+ def drain(self):
+ return self._request("POST", f"/runs/{parse.quote(self.run_id)}/drain")
+
+ def cancel_run(self):
+ return self._request("POST", f"/runs/{parse.quote(self.run_id)}/cancel")
+
+
+def load_group_result(run_id, group_request, result, *, allow_synthetic=False):
+ """Validate a GroupResult and load its trajectory manifests in slot order."""
+ if not isinstance(result, dict):
+ raise ServiceRolloutError("group result must be a mapping")
+ if result.get("status") != "complete":
+ raise ServiceRolloutError(
+ f"group is {result.get('status')!r}, not complete: {result.get('error', '')}")
+ if result.get("run_id") != run_id or result.get("group_id") != group_request["group_id"]:
+ raise ServiceRolloutError("group result identity does not match the request")
+ if result.get("request_digest") != digest(group_request):
+ raise ServiceRolloutError("group result digest does not match the request")
+ slots = result.get("slots")
+ expected = [slot["slot_id"] for slot in group_request["slots"]]
+ if (not isinstance(slots, list) or len(slots) != len(expected) or len(slots) < 2
+ or [slot.get("slot_id") for slot in slots] != expected):
+ raise ServiceRolloutError("group result is incomplete or reordered")
+ trajectories = []
+ for descriptor, slot in zip(group_request["slots"], slots):
+ path = Path(slot["manifest_path"])
+ try:
+ payload = read_json(path)
+ except (OSError, ValueError) as exc:
+ raise ServiceRolloutError(f"cannot read slot manifest: {exc}") from exc
+ if digest(payload) != slot.get("manifest_sha256"):
+ raise ServiceRolloutError(f"slot {descriptor['slot_id']} manifest digest mismatch")
+ identity = payload.get("identity", {})
+ if (identity.get("policy_version") != group_request["policy_version"]
+ or identity.get("task_digest") != group_request["task"]["task_digest"]
+ or identity.get("run_id") != run_id
+ or identity.get("group_id") != group_request["group_id"]
+ or identity.get("slot_id") != descriptor["slot_id"]):
+ raise ServiceRolloutError(
+ f"slot {descriptor['slot_id']} identity differs from the request")
+ try:
+ require_ready(payload, allow_synthetic=allow_synthetic)
+ except ValueError as exc:
+ raise ServiceRolloutError(f"slot {descriptor['slot_id']}: {exc}") from exc
+ trajectories.append(payload)
+ return trajectories
diff --git a/harborrl/cli.py b/harborrl/cli.py
index 55530e0a1..0ea67a8a0 100644
--- a/harborrl/cli.py
+++ b/harborrl/cli.py
@@ -4,18 +4,35 @@
import argparse
import json
import sys
-
-ALGORITHM_CONTRACTS = {"grpo": "slime-v0.3.2-native-v1"}
+from pathlib import Path
def main(argv=None):
argv = list(sys.argv[1:] if argv is None else argv)
+ if argv and argv[0] == "web":
+ from harborrl.webui.cli import main as web_main
+
+ return web_main(argv[1:])
+ if argv and argv[0] in {"options", "validate", "init"}:
+ from harborrl.options.cli import main as options_main
+
+ return options_main(argv)
+ if argv and argv[0] == "list":
+ from harborrl.math_registry import list_components
+
+ choices = {"models", "algorithms", "tasks", "harnesses"}
+ if len(argv) != 2 or argv[1] not in choices:
+ parser = argparse.ArgumentParser(prog="harborrl list")
+ parser.error("choose one of: " + ", ".join(sorted(choices)))
+ print(json.dumps(list_components()[argv[1]], indent=2))
+ return 0
if argv and argv[0] == "hub":
from harborrl.tasks.cli import main as hub_main
return hub_main(argv[1:])
parser = argparse.ArgumentParser(
- prog="harborrl", description=__doc__, allow_abbrev=False
+ prog="harborrl", description=__doc__, allow_abbrev=False,
+ epilog="Configuration tools: harborrl options --help; harborrl validate --help; harborrl init --help; harborrl web --help."
)
parser.add_argument("-model", help="Slime model preset to select")
parser.add_argument("-harness", help="agent harness to select")
@@ -24,10 +41,13 @@ def main(argv=None):
parser.add_argument("command", choices=["train", "doctor"])
parser.add_argument("config_path", nargs="?", help="YAML configuration file")
parser.add_argument("--config", dest="config_option", help="YAML configuration file")
+ parser.add_argument("--deployment", help="deployment YAML describing the manager, GPU target and rollout workers")
parser.add_argument("--set", action="append", default=[])
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args(argv)
from harborrl.config import launch_plan, load_config
+ from harborrl.deployment.schema import embedded_local, load_deployment
+ from harborrl.deployment.schema import summary as deployment_summary
if args.config_path and args.config_option:
parser.error(
@@ -46,20 +66,36 @@ def main(argv=None):
if args.harness is not None:
overrides.append(f"harness.name={args.harness.replace('-', '_')}")
if args.alg is not None:
- contract = ALGORITHM_CONTRACTS.get(args.alg)
- if contract is None:
+ from harborrl.math_registry import MATH_REGISTRY
+
+ matches = [
+ spec for spec in MATH_REGISTRY.algorithms.items()
+ if args.alg in (spec.id,)
+ ]
+ if not matches:
parser.error(
f"unsupported algorithm: {args.alg}; "
- f"supported algorithms: {', '.join(sorted(ALGORITHM_CONTRACTS))}"
+ f"supported algorithms: "
+ f"{', '.join(spec.id for spec in MATH_REGISTRY.algorithms.items())}"
)
+ contract = next(iter(matches[0].training_backend_contracts))
overrides.append(f"training.backend_contract={contract}")
try:
- plan = launch_plan(load_config(
+ config = load_config(
config_path,
overrides,
task_ids=None if args.task is None else [args.task],
- ))
+ )
+ deployment, deployment_source = load_deployment(args.deployment)
+ if deployment_source == "embedded-local":
+ deployment = embedded_local(
+ run_root=str(Path(config["output"]["root"]).resolve()),
+ workers=config["harbor"]["workers"],
+ expected_gpus=config["deployment"]["num_gpus"],
+ )
+ plan = launch_plan(config)
+ plan["deployment"] = deployment_summary(deployment, deployment_source)
except (TypeError, ValueError, OSError) as exc:
parser.error(str(exc))
if args.dry_run:
diff --git a/harborrl/config/native.py b/harborrl/config/native.py
index 227f6e4d3..50b3ddb86 100644
--- a/harborrl/config/native.py
+++ b/harborrl/config/native.py
@@ -1,19 +1,18 @@
"""Strict native schema 2; local paths resolve relative to the config file."""
-import json
import os
import re
-import sys
from pathlib import Path
from urllib.parse import urlsplit
import yaml
-from harborrl.rollout.harbor_job.bindings import claude_config
-from harborrl.trajectories.native import RewardProfile
+from harborrl.consistency.config import DEFAULTS as CONSISTENCY_DEFAULTS
+from harborrl.consistency.config import validate as validate_consistency
FIELDS = {
+ 'consistency': set(CONSISTENCY_DEFAULTS),
'execution': {'backend'}, 'tasks': {'catalog'}, 'harness': {'name', 'profile'},
- 'harbor': {'python', 'version', 'workers'},
+ 'harbor': {'python', 'version', 'workers', 'runner_transport', 'environment'},
'gateway': {'host', 'port', 'advertised_url', 'tokenizer_digest', 'template_digest', 'audited_raw_logprobs'},
'model': {'checkpoint', 'reference', 'args_file'},
'training': {'num_rollout', 'learning_rate', 'save_interval', 'backend_contract'},
@@ -21,6 +20,7 @@
'deployment': {'layout', 'num_gpus', 'actor_gpus', 'rollout_gpus', 'actor_tensor_parallel_size', 'rollout_gpus_per_engine'},
'output': {'root'},
}
+OPTIONAL = {'harbor': {'runner_transport', 'environment'}}
class SelectedConfig(dict):
@@ -32,7 +32,10 @@ def __init__(self, config, task_ids):
def load_config(path, overrides=None, task_ids=None):
"""Load one schema-2 YAML file and apply strict ``section.key=value`` overrides."""
path = Path(path).resolve()
- config = yaml.safe_load(path.read_text())
+ try:
+ config = yaml.safe_load(path.read_text())
+ except yaml.YAMLError as exc:
+ raise ValueError(f'configuration YAML: {exc}') from exc
if not isinstance(config, dict):
raise TypeError('configuration must be a mapping')
if config.get('schema_version') != 2:
@@ -45,8 +48,12 @@ def load_config(path, overrides=None, task_ids=None):
if (not separator or field not in FIELDS.get(section_name, ())
or not isinstance(section, dict)):
raise ValueError(f'unknown native override: {key}')
- section[field] = yaml.safe_load(value)
+ try:
+ section[field] = yaml.safe_load(value)
+ except yaml.YAMLError as exc:
+ raise ValueError(f'{key}: invalid YAML override') from exc
config = validate(config, path)
+ validate_files(config)
if task_ids is None:
return config
if (not isinstance(task_ids, (list, tuple)) or not task_ids
@@ -57,16 +64,19 @@ def load_config(path, overrides=None, task_ids=None):
def validate(config, path):
- from harborrl.trajectories.native import read_json
+ if not isinstance(config, dict):
+ raise TypeError('configuration must be a mapping')
+ config['consistency'] = validate_consistency(config.get('consistency'))
if set(config) != set(FIELDS) | {'schema_version'} or type(config['schema_version']) is not int or config['schema_version'] != 2:
raise ValueError('native schema requires exactly the documented schema 2 sections')
+ if not isinstance(config['training'], dict):
+ raise TypeError('training must be a mapping')
config['training'].setdefault(
'backend_contract', 'slime-v0.3.2-native-v1')
for section, fields in FIELDS.items():
- if not isinstance(config[section], dict) or set(config[section]) != fields:
+ actual = set(config[section]) if isinstance(config[section], dict) else set()
+ if not isinstance(config[section], dict) or not fields - OPTIONAL.get(section, set()) <= actual <= fields:
raise ValueError(f'{section} requires exactly {sorted(fields)}')
- if config['execution']['backend'] != 'harbor_job' or config['harness']['name'] != 'claude_code' or config['harbor']['version'] != '0.23.0':
- raise ValueError('native requires harbor_job / claude_code / Harbor 0.23.0')
for section, key in [('tasks','catalog'), ('harness','profile'), ('harbor','python'),
('model','checkpoint'), ('model','reference'), ('output','root')]:
value = config[section][key]
@@ -82,133 +92,90 @@ def validate(config, path):
raise ValueError(f'{section}.{key} must be a positive integer')
from harborrl.backends.slime_v032.versions import CONFIG_CONTRACTS
from harborrl.trajectories.native import finite
- if config['training']['backend_contract'] not in CONFIG_CONTRACTS:
+ if (not isinstance(config['training']['backend_contract'], str)
+ or config['training']['backend_contract'] not in CONFIG_CONTRACTS):
raise ValueError(f"training.backend_contract must be one of {sorted(CONFIG_CONTRACTS)}")
if not finite(config['training']['learning_rate']) or config['training']['learning_rate'] <= 0:
- raise ValueError('positive finite learning_rate required')
+ raise ValueError('training.learning_rate must be positive and finite')
g, d, s = config['gateway'], config['deployment'], config['sampling']
if type(g['audited_raw_logprobs']) is not bool or not 0 < g['port'] < 65536:
- raise ValueError('invalid gateway audit flag or port')
+ raise ValueError('gateway.audited_raw_logprobs must be boolean and gateway.port must be in 1..65535')
for key in ('host','tokenizer_digest','template_digest'):
if not isinstance(g[key], str) or not g[key].strip():
raise ValueError(f'gateway.{key} required')
+ if not isinstance(g['advertised_url'], str):
+ raise TypeError('gateway.advertised_url must be a string')
advertised = urlsplit(g['advertised_url'])
if (advertised.scheme not in ('http', 'https') or not advertised.hostname
or advertised.username or advertised.password or advertised.query or advertised.fragment
or advertised.path not in ('', '/') or advertised.port != g['port']):
raise ValueError('advertised gateway must be a plain origin and match listener port')
- if s['group_size'] < 2 or s['max_tokens'] >= s['max_context']:
- raise ValueError('invalid group or context budget')
- if d['layout'] not in ('split','colocate'):
+ if s['group_size'] < 2:
+ raise ValueError('sampling.group_size must be at least 2')
+ if s['max_tokens'] >= s['max_context']:
+ raise ValueError('sampling.max_tokens must be below sampling.max_context')
+ if not isinstance(d['layout'], str) or d['layout'] not in ('split','colocate'):
raise ValueError('layout must be split or colocate')
if d['layout'] == 'split' and d['actor_gpus'] + d['rollout_gpus'] > d['num_gpus']:
- raise ValueError('GPU budget exceeded')
+ raise ValueError('GPU budget exceeded: deployment.actor_gpus + deployment.rollout_gpus > deployment.num_gpus')
if d['layout'] == 'colocate' and not d['actor_gpus'] == d['rollout_gpus'] == d['num_gpus']:
raise ValueError('colocate GPU counts must match')
if d['actor_gpus'] % d['actor_tensor_parallel_size'] or d['rollout_gpus'] % d['rollout_gpus_per_engine']:
- raise ValueError('GPU counts must be divisible by TP')
+ raise ValueError('deployment GPU counts must be divisible by their tensor parallel sizes')
preset = config['model']['args_file']
if not isinstance(preset,str) or not re.fullmatch(r'[A-Za-z0-9_-]+',preset):
raise ValueError('model.args_file must name a Slime model preset; SLIME_DIR is checked by doctor')
for key in ('tokenizer_digest', 'template_digest'):
if not re.fullmatch(r'[0-9a-f]{64}', g[key]):
raise ValueError(f'gateway.{key} must be a SHA-256 digest')
- workers = config['harbor']['workers']
- if (not isinstance(workers, list) or not workers or any(
+ harbor = config['harbor']
+ transport = harbor.get('runner_transport', 'ssh')
+ if transport not in ('ssh', 'local'):
+ raise ValueError('harbor.runner_transport must be ssh or local')
+ environment = harbor.get('environment', {'runtime': 'docker'})
+ if not isinstance(environment, dict) or environment.get('runtime') not in ('docker', 'sandbox'):
+ raise ValueError('harbor.environment.runtime must be docker or sandbox')
+ expected = {'runtime', 'config'} if environment['runtime'] == 'sandbox' else {'runtime'}
+ if set(environment) != expected:
+ raise ValueError('invalid harbor.environment fields')
+ if environment['runtime'] == 'sandbox':
+ if not isinstance(environment['config'], str) or not environment['config']:
+ raise ValueError('sandbox config path required')
+ environment['config'] = str((Path(path).parent / environment['config']).resolve())
+ workers = harbor['workers']
+ if (not isinstance(workers, list) or (transport == 'ssh' and not workers) or any(
not isinstance(worker, str) or not re.fullmatch(r'[A-Za-z0-9_.:+@-]+', worker) or worker.startswith('-')
for worker in workers)):
raise ValueError('harbor.workers must contain nonempty SSH destinations')
- claude_config(read_json(config['harness']['profile']), g['advertised_url'], 'validation-only')
+ if transport == 'local' and workers:
+ raise ValueError('local runner requires empty harbor.workers')
+ from harborrl.math_registry import select_math, validate_combination
+ validate_combination(select_math(config), config)
return config
-def catalog(config):
- from harborrl.data.harbor.native_inspector import inspect_native
+def validate_files(config):
+ """Check harness profile files separately from pure schema validation."""
+ from harborrl.math_registry import select_math
from harborrl.trajectories.native import read_json
- path = Path(config['tasks']['catalog'])
- entries = read_json(path)
- if not isinstance(entries,list) or not entries:
- raise ValueError('catalog must be a nonempty JSON list')
- seen = set()
- for entry in entries:
- if (set(entry) != {'id','revision','path','task_digest','reward_profile'}
- or not isinstance(entry['id'], str) or not entry['id'].strip()
- or not isinstance(entry['revision'], str) or not entry['revision'].strip()
- or entry['id'] in seen):
- raise ValueError('invalid or duplicate catalog identity')
- if (not isinstance(entry['path'], str) or not entry['path'].strip()
- or not isinstance(entry['task_digest'], str)
- or not re.fullmatch(r'[0-9a-f]{64}', entry['task_digest'])
- or not isinstance(entry['reward_profile'], dict)):
- raise ValueError(f'invalid native task lock: {entry["id"]}')
- seen.add(entry['id'])
- entry['path'] = str((path.parent / entry['path']).resolve())
- report = inspect_native(entry['path'], expected_digest=entry['task_digest'])
- if report['status'] != 'NEEDS_PROBE':
- raise ValueError(f"task preflight failed: {entry['id']}: {report['reasons']}")
- RewardProfile(**entry['reward_profile'])
- selected = set(getattr(config, 'task_ids', ()))
- if selected:
- missing = selected - {entry['id'] for entry in entries}
- if missing:
- raise ValueError(f'task selector not found in catalog: {min(missing)}')
- entries = [entry for entry in entries if entry['id'] in selected]
- return entries
+ harness = select_math(config).harness
+ try:
+ harness.adapter.validate_profile(
+ read_json(config['harness']['profile']),
+ config['gateway']['advertised_url'],
+ )
+ except (TypeError, ValueError, KeyError) as exc:
+ raise ValueError(f'harness.profile: {exc}') from exc
+ return config
+
+
+def catalog(config):
+ """Compatibility entry point for task lock validation."""
+ from harborrl.tasks.catalog import catalog as inspect_catalog
+ return inspect_catalog(config)
def launch_plan(config):
- from harborrl.backends.slime_v032.versions import runtime_contract_id
- from harborrl.config import ROOT
- catalog(config)
- c = config
- worker_overrides = os.environ.get('HARBORRL_NATIVE_TASK_WORKER_OVERRIDES', '{}')
- try:
- overrides = json.loads(worker_overrides)
- valid_workers = set(c['harbor']['workers'])
- if (not isinstance(overrides, dict) or any(not isinstance(key, str) or value not in valid_workers
- for key, value in overrides.items())):
- raise ValueError
- except (ValueError, json.JSONDecodeError) as exc:
- raise ValueError('invalid HARBORRL_NATIVE_TASK_WORKER_OVERRIDES') from exc
- environment = {
- 'DATASET': 'native', 'ALGO': 'grpo', 'HARNESS_OPTION': 'native-claude-code',
- 'HF_CKPT': c['model']['checkpoint'], 'REF_LOAD': c['model']['reference'],
- 'MODEL_ARGS_FILE': c['model']['args_file'], 'MODEL_TAG': c['model']['args_file'],
- 'NUM_ROLLOUT': c['training']['num_rollout'], 'SAVE_INTERVAL': c['training']['save_interval'],
- 'TRAIN_PYTHON': sys.executable,
- 'ROLLOUT_BATCH_SIZE': c['sampling']['groups_per_batch'],
- 'N_SAMPLES': c['sampling']['group_size'],
- 'ROLLOUT_MAX_RESPONSE_LEN': c['sampling']['max_tokens'],
- 'ROLLOUT_MAX_CONTEXT_LEN': c['sampling']['max_context'],
- 'NUM_GPUS': c['deployment']['num_gpus'], 'ACTOR_GPUS': c['deployment']['actor_gpus'],
- 'ROLLOUT_GPUS': c['deployment']['rollout_gpus'],
- 'TP_SIZE': c['deployment']['actor_tensor_parallel_size'],
- 'ROLLOUT_NUM_GPUS_PER_ENGINE': c['deployment']['rollout_gpus_per_engine'],
- 'HARBORRL_GPU_LAYOUT': c['deployment']['layout'],
- 'HARBORRL_STRUCTURED_LAUNCH': '1', 'HARBORRL_VERIFY_POLICY_POOL': '1',
- 'HARBORRL_SKIP_GLOBAL_CLEANUP': '1', 'SLIME_RAY_PLACEMENT_GPU_PROBE': '1',
- 'PYTORCH_CUDA_ALLOC_CONF': ('max_split_size_mb:2048' if c['deployment']['layout'] == 'colocate'
- else 'max_split_size_mb:2048,expandable_segments:True'),
- 'HARBORRL_NATIVE_ROLLOUT': '1',
- 'HARBORRL_NATIVE_SLIME_CONTRACT': runtime_contract_id(c['training']['backend_contract']),
- 'HARBORRL_NATIVE_RUNNER_PYTHON': c['harbor']['python'],
- 'HARBORRL_NATIVE_RUNNER_WORKERS': json.dumps(c['harbor']['workers']),
- 'HARBORRL_NATIVE_TASK_WORKER_OVERRIDES': json.dumps(overrides, sort_keys=True),
- 'HARBORRL_NATIVE_PROFILE': c['harness']['profile'],
- 'HARBORRL_NATIVE_CATALOG': c['tasks']['catalog'],
- 'HARBORRL_NATIVE_GATEWAY_HOST': c['gateway']['host'],
- 'HARBORRL_NATIVE_GATEWAY_PORT': c['gateway']['port'],
- 'HARBORRL_NATIVE_GATEWAY_URL': c['gateway']['advertised_url'],
- 'HARBORRL_NATIVE_TOKENIZER_DIGEST': c['gateway']['tokenizer_digest'],
- 'HARBORRL_NATIVE_TEMPLATE_DIGEST': c['gateway']['template_digest'],
- 'HARBORRL_NATIVE_AUDITED_LOGPROBS': str(int(c['gateway']['audited_raw_logprobs'])),
- 'HARBORRL_NATIVE_MAX_ATTEMPTS': c['sampling']['max_attempts'],
- 'HARBORRL_NATIVE_DRAIN_TIMEOUT': '30',
- 'RUNS_ROOT': c['output']['root'], 'WANDB_ENABLE': '0',
- }
- return {'config': config, 'command': [sys.executable, '-m', 'harborrl.platform.native_train'],
- 'environment': {key: str(value) for key, value in environment.items()},
- 'training_command': ['bash', str(ROOT / 'harborrl/platform/slime_train.sh')],
- 'required_services': ['GPU Ray / SGLang / Messages Gateway', 'external CPU Harbor Docker workers'],
- 'launcher': 'native Claude Code → Messages Gateway → Harbor verifier → Slime GRPO',
- 'layout': {'mode': config['deployment']['layout'], 'offload': config['deployment']['layout']=='colocate'}}
+ """Compatibility entry point for backend launch mapping."""
+ from harborrl.backends.slime_v032.config import launch_plan as build_plan
+ return build_plan(config)
diff --git a/harborrl/consistency/__init__.py b/harborrl/consistency/__init__.py
new file mode 100644
index 000000000..8031995e5
--- /dev/null
+++ b/harborrl/consistency/__init__.py
@@ -0,0 +1 @@
+"""Backend consistency evidence, independent of optimization objectives."""
diff --git a/harborrl/consistency/actor.py b/harborrl/consistency/actor.py
new file mode 100644
index 000000000..e332fc4d1
--- /dev/null
+++ b/harborrl/consistency/actor.py
@@ -0,0 +1,88 @@
+"""Small Slime actor extension: snapshot trained-model canaries before weight sync."""
+import os
+from pathlib import Path
+
+import torch
+import torch.distributed as dist
+from slime.backends.megatron_utils.actor import MegatronTrainRayActor
+from slime.backends.megatron_utils.data import DataIterator
+from slime.backends.megatron_utils.loss import get_log_probs_and_entropy
+from slime.backends.megatron_utils.model import forward_only
+from slime.backends.megatron_utils.update_weight.common import named_params_and_buffers
+from slime.utils.distributed_utils import get_gloo_group
+from slime.utils.reloadable_process_group import (
+ destroy_process_groups,
+ reload_process_groups,
+)
+
+from harborrl.trajectories.native import digest, publish
+
+
+class ConsistencyActor(MegatronTrainRayActor):
+ def update_weights(self):
+ if self.args.offload_train:
+ self.wake_up()
+ self._switch_model("actor")
+ from harborrl.rollout.runtime import tokenizer_fingerprint
+ error = None
+ try:
+ actual = tokenizer_fingerprint(self.tokenizer)
+ expected = (os.environ['HARBORRL_NATIVE_TOKENIZER_DIGEST'], os.environ['HARBORRL_NATIVE_TEMPLATE_DIGEST'])
+ if actual != expected:
+ raise ValueError('Trainer tokenizer/template differs from rollout launch identity')
+ except (ValueError, TypeError, KeyError) as exc:
+ error = str(exc)
+ errors = [None] * dist.get_world_size()
+ dist.all_gather_object(errors, error, group=get_gloo_group())
+ if any(errors):
+ raise ValueError(f'Invalid trainer identity: {errors}')
+ cases = []
+ # Literal token fixtures intentionally bypass all serving-side text templating.
+ for prompt, answer in (("Compute 1 + 1 =", " 2.\n"), ("def square(x):\n", " return x * x\n")):
+ p = self.tokenizer.encode(prompt, add_special_tokens=True)
+ r = self.tokenizer.encode(answer, add_special_tokens=False)
+ if not p or not r:
+ raise ValueError("Tokenizer produced an empty canary")
+ cases.append({"prompt_ids": p, "response_ids": r})
+ device = next(self.model[0].parameters()).device
+ data = {"tokens": [torch.tensor(c["prompt_ids"] + c["response_ids"], device=device) for c in cases],
+ "loss_masks": [torch.ones(len(c["response_ids"]), dtype=torch.int, device=device) for c in cases],
+ "total_lengths": [len(c["prompt_ids"]) + len(c["response_ids"]) for c in cases],
+ "response_lengths": [len(c["response_ids"]) for c in cases]}
+ output = forward_only(get_log_probs_and_entropy, self.args, self.model,
+ [DataIterator(data, [[i] for i in range(len(cases))])], [len(cases)])
+ for case, values in zip(cases, output["log_probs"], strict=True):
+ case["logprobs"] = values.detach().float().cpu().tolist()
+ fingerprints = []
+ for name, parameter in named_params_and_buffers(self.args, self.model):
+ flat = parameter.detach().reshape(-1)
+ indices = torch.linspace(0, max(0, flat.numel() - 1), min(16, flat.numel()), device=flat.device).long()
+ fingerprints.append({"name": name, "shape": list(parameter.shape), "dtype": str(parameter.dtype),
+ "sample": flat[indices].float().cpu().tolist()})
+ local = {"rank": dist.get_rank(), "fingerprint": digest(fingerprints), "tensor_count": len(fingerprints)}
+ shards = [None] * dist.get_world_size()
+ dist.all_gather_object(shards, local, group=get_gloo_group())
+ config = self.hf_config.to_dict()
+ reference = {"schema_version": "harborrl-sync-reference-v1", "cases": cases,
+ "model_config_digest": digest(config), "model_config": config,
+ "tokenizer_vocabulary_digest": digest(sorted(self.tokenizer.get_vocab().items())),
+ "parameter_shards": shards, "fingerprint_semantics": "16 deterministic values per tensor; not a full checksum"}
+ if self.args.offload_train:
+ self.sleep()
+ super().update_weights()
+ if self.args.offload_train:
+ reload_process_groups()
+ reference["weight_version"] = str(self.weight_updater.weight_version)
+ reference["digest"] = digest(reference)
+ error = None
+ if dist.get_rank() == 0:
+ try:
+ publish(Path(os.environ["RUN_DIR"]) / "consistency" / f"sync-{reference['weight_version']}.json", reference)
+ except (OSError, ValueError) as exc:
+ error = str(exc)
+ errors = [None] * dist.get_world_size()
+ dist.all_gather_object(errors, error, group=get_gloo_group())
+ if self.args.offload_train:
+ destroy_process_groups()
+ if any(errors):
+ raise ValueError(f"Failed to publish synchronized model evidence: {errors}")
diff --git a/harborrl/consistency/config.py b/harborrl/consistency/config.py
new file mode 100644
index 000000000..f2317e49a
--- /dev/null
+++ b/harborrl/consistency/config.py
@@ -0,0 +1,51 @@
+"""Explicit deployment-specific tolerances; no universal numeric threshold."""
+import json
+import os
+
+from harborrl.trajectories.native import finite
+
+DEFAULTS = {"mode": "off", "max_abs_delta": None, "max_mean_abs_delta": None,
+ "bad_token_delta": None, "max_bad_fraction": 0.0, "worst_tokens": 8}
+
+
+def validate(value=None):
+ if value is None:
+ value = {}
+ if not isinstance(value, dict) or set(value) - set(DEFAULTS):
+ raise ValueError("Unknown consistency configuration fields")
+ result = {**DEFAULTS, **value}
+ if result["mode"] not in ("off", "audit", "enforce"):
+ raise ValueError("consistency.mode must be off, audit or enforce")
+ for key in ("max_abs_delta", "max_mean_abs_delta", "bad_token_delta"):
+ if result[key] is not None and (not finite(result[key]) or result[key] < 0):
+ raise ValueError(f"consistency.{key} must be nonnegative and finite")
+ if result["mode"] == "enforce" and result["max_abs_delta"] is None:
+ raise ValueError("enforce requires an explicit max_abs_delta for this deployment")
+ if not finite(result["max_bad_fraction"]) or not 0 <= result["max_bad_fraction"] <= 1:
+ raise ValueError("max_bad_fraction must be in [0, 1]")
+ if type(result["worst_tokens"]) is not int or not 0 <= result["worst_tokens"] <= 32:
+ raise ValueError("worst_tokens must be an integer in [0, 32]")
+ return result
+
+
+def environment():
+ return validate(json.loads(os.environ.get("HARBORRL_CONSISTENCY", "{}")))
+
+
+def require_raw_sampling(sampling):
+ allowed = {"temperature", "top_p", "top_k", "max_new_tokens", "stop", "raw_logprobs"}
+ if (not isinstance(sampling, dict) or set(sampling) - allowed
+ or sampling.get("temperature") != 1 or sampling.get("top_p") != 1
+ or sampling.get("top_k", -1) not in (-1, 0)
+ or sampling.get("raw_logprobs", True) is not True):
+ raise ValueError("Consistency v1 requires untransformed raw-policy sampling")
+
+
+def violations(metrics, config):
+ errors = []
+ for option, field in (("max_abs_delta", "max_abs_delta"), ("max_mean_abs_delta", "mean_abs_delta")):
+ if config[option] is not None and metrics[field] > config[option]:
+ errors.append(f"{field}={metrics[field]:.8g} exceeds {config[option]}")
+ if config["bad_token_delta"] is not None and metrics["bad_fraction"] > config["max_bad_fraction"]:
+ errors.append(f"bad_fraction={metrics['bad_fraction']:.8g} exceeds {config['max_bad_fraction']}")
+ return errors
diff --git a/harborrl/consistency/metrics.py b/harborrl/consistency/metrics.py
new file mode 100644
index 000000000..4057e8c97
--- /dev/null
+++ b/harborrl/consistency/metrics.py
@@ -0,0 +1,67 @@
+"""Mergeable token statistics with bounded-memory percentile upper bounds."""
+import bisect
+import math
+
+from harborrl.trajectories.native import finite
+
+# Logarithmic buckets retain sub-micro precision near zero, with no token all-gather.
+EDGES = [0.0, *[2 ** (-24 + i / 8) for i in range(321)]]
+
+
+def statistics(rows, *, threshold=None, worst=8):
+ result = {"count": 0, "signed_sum": 0.0, "abs_sum": 0.0, "max_abs_delta": 0.0,
+ "min_log_ratio": None, "max_log_ratio": None, "bad_count": 0,
+ "histogram": [0] * (len(EDGES) + 1), "worst": []}
+ for row_index, (current, behavior, mask) in enumerate(rows):
+ if len(current) != len(behavior) or len(mask) != len(current) or not len(current):
+ raise ValueError("Probability/mask length mismatch")
+ if any(flag not in (0, 1) for flag in mask):
+ raise ValueError("Loss mask must be binary")
+ for position, (p, q, flag) in enumerate(zip(current, behavior, mask, strict=True)):
+ if not finite(p) or not finite(q) or p > 0 or q > 0:
+ raise ValueError("Log probabilities must be finite and nonpositive")
+ if not flag:
+ continue
+ delta = float(p) - float(q)
+ absolute = abs(delta)
+ if not math.isfinite(delta):
+ raise ValueError("Nonfinite log-probability difference")
+ result["count"] += 1
+ result["signed_sum"] += delta
+ result["abs_sum"] += absolute
+ result["max_abs_delta"] = max(result["max_abs_delta"], absolute)
+ result["min_log_ratio"] = delta if result["min_log_ratio"] is None else min(result["min_log_ratio"], delta)
+ result["max_log_ratio"] = delta if result["max_log_ratio"] is None else max(result["max_log_ratio"], delta)
+ result["bad_count"] += int(threshold is not None and absolute > threshold)
+ result["histogram"][bisect.bisect_left(EDGES, absolute)] += 1
+ if worst:
+ result["worst"].append({"row": row_index, "position": position, "delta": delta})
+ result["worst"] = sorted(result["worst"], key=lambda x: abs(x["delta"]), reverse=True)[:worst]
+ if not result["count"]:
+ raise ValueError("No valid response tokens")
+ if not math.isfinite(result["abs_sum"]) or not math.isfinite(result["signed_sum"]):
+ raise ValueError("Probability statistics overflow")
+ return result
+
+
+def merge(parts):
+ if not parts:
+ raise ValueError("No contributing ranks")
+ count = sum(p["count"] for p in parts)
+ bins = [sum(p["histogram"][i] for p in parts) for i in range(len(EDGES) + 1)]
+ maximum = max(p["max_abs_delta"] for p in parts)
+ def percentile(fraction):
+ target, seen = math.ceil(count * fraction), 0
+ for index, n in enumerate(bins):
+ seen += n
+ if seen >= target:
+ return min(EDGES[index], maximum) if index < len(EDGES) else maximum
+ raise ValueError("Corrupt histogram")
+ return {"token_count": count, "signed_mean_delta": math.fsum(p["signed_sum"] for p in parts) / count,
+ "mean_abs_delta": math.fsum(p["abs_sum"] for p in parts) / count,
+ "max_abs_delta": maximum, "p95_abs_delta_upper": percentile(.95), "p99_abs_delta_upper": percentile(.99),
+ "min_log_ratio": min(p["min_log_ratio"] for p in parts),
+ "max_log_ratio": max(p["max_log_ratio"] for p in parts),
+ "bad_fraction": sum(p["bad_count"] for p in parts) / count,
+ "percentile_semantics": "logarithmic-histogram upper bound; not exact quantiles",
+ "gap_semantics": "fixed-prefix token log-ratio; not full-sequence KL"}
diff --git a/harborrl/consistency/probe.py b/harborrl/consistency/probe.py
new file mode 100644
index 000000000..a218337b1
--- /dev/null
+++ b/harborrl/consistency/probe.py
@@ -0,0 +1,35 @@
+"""Replay a Megatron fixed-token reference against an explicit engine pool."""
+import argparse
+import json
+
+from harborrl.trajectories.native import digest, publish, read_json
+
+from .config import validate
+from .sglang import check_pool, http_json
+
+
+def main(argv=None):
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--reference', required=True)
+ parser.add_argument('--engine', action='append', required=True)
+ parser.add_argument('--config', required=True, help='JSON consistency mapping with explicit tolerances')
+ parser.add_argument('--output', required=True)
+ args = parser.parse_args(argv)
+ settings = validate(read_json(args.config))
+ if settings['mode'] == 'off':
+ parser.error('probe mode must be audit or enforce')
+ reference = read_json(args.reference)
+ if reference.get('digest') != digest({k: v for k, v in reference.items() if k != 'digest'}):
+ raise ValueError('Corrupt sync reference')
+ states = [{'endpoint': endpoint.rstrip('/'), 'healthy': True,
+ 'weight_version': http_json(endpoint.rstrip('/') + '/get_weight_version')['weight_version']}
+ for endpoint in args.engine]
+ report = check_pool(states, reference, settings)
+ report['reference_digest'] = reference['digest']
+ publish(args.output, report)
+ print(json.dumps(report, indent=2))
+ return int(report['status'] == 'failed')
+
+
+if __name__ == '__main__':
+ raise SystemExit(main())
diff --git a/harborrl/consistency/report.py b/harborrl/consistency/report.py
new file mode 100644
index 000000000..0b3962894
--- /dev/null
+++ b/harborrl/consistency/report.py
@@ -0,0 +1,51 @@
+"""Collect errors before raising so every participating rank makes the same decision."""
+from pathlib import Path
+
+from harborrl.trajectories.native import publish
+
+from .config import violations
+from .metrics import merge, statistics
+
+
+def assess(rows, config, *, metadata=None):
+ try:
+ return {"statistics": statistics(rows, threshold=config["bad_token_delta"], worst=config["worst_tokens"]),
+ "error": None, "metadata": metadata or {}}
+ except (ValueError, TypeError, OverflowError) as exc:
+ return {"statistics": None, "error": f"{type(exc).__name__}: {exc}", "metadata": metadata or {}}
+
+
+def decide(parts, config, *, phase):
+ structural = [p["error"] for p in parts if p["error"]]
+ contributing = [p for p in parts if p.get("contribute", True)]
+ metrics = None if structural else merge([p["statistics"] for p in contributing])
+ numeric = violations(metrics, config) if metrics else []
+ return {"schema_version": "harborrl-consistency-v1", "phase": phase, "mode": config["mode"],
+ "status": "failed" if structural or (numeric and config["mode"] == "enforce") else "warning" if numeric else "passed",
+ "structural_errors": structural, "numeric_violations": numeric, "metrics": metrics,
+ "config": config, "ranks": [{"metadata": p["metadata"], "error": p["error"],
+ "contribute": p.get("contribute", True),
+ "worst": p["statistics"]["worst"] if p["statistics"] else []} for p in parts]}
+
+
+def distributed_gate(part, config, path, *, phase, group=None):
+ import torch.distributed as dist
+ active = dist.is_available() and dist.is_initialized()
+ parts = [part]
+ if active:
+ parts = [None] * dist.get_world_size(group)
+ dist.all_gather_object(parts, part, group=group)
+ report = decide(parts, config, phase=phase)
+ write_error = None
+ if not active or dist.get_rank(group) == 0:
+ try:
+ publish(Path(path), report)
+ except (OSError, ValueError) as exc:
+ write_error = str(exc)
+ if active:
+ errors = [None] * dist.get_world_size(group)
+ dist.all_gather_object(errors, write_error, group=group)
+ write_error = next((error for error in errors if error), None)
+ if write_error or report["status"] == "failed":
+ raise ValueError(f"Consistency {phase} rejected: {write_error or report['structural_errors'] or report['numeric_violations']}")
+ return report
diff --git a/harborrl/consistency/sglang.py b/harborrl/consistency/sglang.py
new file mode 100644
index 000000000..d9a5ac654
--- /dev/null
+++ b/harborrl/consistency/sglang.py
@@ -0,0 +1,92 @@
+"""Exact-token teacher-forcing probes against every synchronized inference engine."""
+import json
+from pathlib import Path
+from urllib import request
+
+from harborrl.trajectories.native import digest, finite, publish, read_json
+
+from .config import environment, violations
+from .metrics import merge
+from .report import assess, decide
+
+
+def http_json(url, payload=None, timeout=120):
+ opener = request.build_opener(request.ProxyHandler({}))
+ req = request.Request(url, data=json.dumps(payload).encode() if payload is not None else None,
+ headers={"Content-Type": "application/json"})
+ with opener.open(req, timeout=timeout) as response:
+ return json.load(response)
+
+
+def score(endpoint, prompt, response, version, *, transport=http_json):
+ tokens = prompt + response
+ payload = {"input_ids": tokens, "sampling_params": {"temperature": 1.0, "top_p": 1.0,
+ "top_k": -1, "max_new_tokens": 0}, "return_logprob": True, "logprob_start_len": 0}
+ output = transport(endpoint.rstrip("/") + "/generate", payload)
+ meta = output.get("meta_info", {})
+ if str(meta.get("weight_version", "")) != str(version):
+ raise ValueError("Canary generation-time weight version mismatch")
+ rows = meta.get("input_token_logprobs")
+ if (not isinstance(rows, list) or len(rows) != len(tokens)
+ or any(not isinstance(row, (tuple, list)) or len(row) < 2 for row in rows)
+ or [row[1] for row in rows] != tokens):
+ raise ValueError("Canary input logprobs must preserve every exact token and position")
+ values = [row[0] for row in rows[len(prompt):]]
+ if any(not finite(value) or value > 0 for value in values):
+ raise ValueError("Invalid canary raw logprob")
+ return values
+
+
+def check_pool(states, reference, config, *, transport=http_json):
+ expected = reference["weight_version"]
+ endpoints = [s["endpoint"] for s in states]
+ if not states or len(set(endpoints)) != len(endpoints):
+ raise ValueError("Canary requires a nonempty unique engine pool")
+ if any(not s.get("healthy") or str(s.get("weight_version")) != str(expected) for s in states):
+ raise ValueError("Canary pool contains stale or unhealthy engines")
+ parts = []
+ for endpoint in endpoints:
+ try:
+ rows = [(case["logprobs"], score(endpoint, case["prompt_ids"], case["response_ids"], expected,
+ transport=transport), [1] * len(case["response_ids"])) for case in reference["cases"]]
+ part = assess(rows, config, metadata={"engine": endpoint, "weight_version": expected})
+ # Detect a version transition across scoring, in addition to each response's version.
+ if str(transport(endpoint + "/get_weight_version").get("weight_version")) != str(expected):
+ raise ValueError("Engine changed weights during canary scoring")
+ except (OSError, ValueError, TypeError, KeyError) as exc:
+ part = {"statistics": None, "error": str(exc), "metadata": {"engine": endpoint}}
+ parts.append(part)
+ report = decide(parts, config, phase="synchronized-pool")
+ # Mean/fraction limits apply independently to every engine.
+ engine_errors = [f"{p['metadata']['engine']}: {error}" for p in parts if p['statistics']
+ for error in violations(merge([p['statistics']]), config)]
+ report['numeric_violations'] = engine_errors
+ if engine_errors and report['status'] != 'failed':
+ report['status'] = 'failed' if config['mode'] == 'enforce' else 'warning'
+ return report
+
+
+def admit_pool(states, run_dir):
+ config = environment()
+ if config["mode"] == "off":
+ return
+ versions = {str(s["weight_version"]) for s in states}
+ if len(versions) != 1:
+ raise ValueError("Cannot admit a mixed-version pool")
+ version = versions.pop()
+ if not version.isdecimal():
+ raise ValueError("Invalid numeric serving version")
+ root = Path(run_dir) / "consistency"
+ reference = read_json(root / f"sync-{version}.json")
+ if reference["weight_version"] != version or reference["digest"] != digest({k: v for k, v in reference.items() if k != "digest"}):
+ raise ValueError("Canary reference identity mismatch")
+ admission = digest({'states': states, 'config': config, 'reference': reference['digest']})
+ path = root / f"pool-{version}-{admission}.json"
+ if path.exists():
+ report = read_json(path)
+ else:
+ report = check_pool(states, reference, config)
+ report["reference_digest"] = reference["digest"]
+ publish(path, report)
+ if report["status"] == "failed":
+ raise ValueError(f"Serving canary rejected version {version}")
diff --git a/harborrl/consistency/slime.py b/harborrl/consistency/slime.py
new file mode 100644
index 000000000..6493e5ea1
--- /dev/null
+++ b/harborrl/consistency/slime.py
@@ -0,0 +1,74 @@
+"""Integration using the locked Slime precomputed log_probs and postprocess hook."""
+import os
+import struct
+from pathlib import Path
+
+from harborrl.trajectories.native import digest, publish, read_json
+
+from .config import environment, require_raw_sampling
+from .report import assess, distributed_gate
+
+
+def evidence_key(tokens, logprobs, mask):
+ q = [struct.unpack("!f", struct.pack("!f", value))[0] for value in logprobs]
+ return digest({"tokens": tokens, "q": q, "mask": mask})
+
+
+def record_turn(turn, tokens, logprobs, mask):
+ config = environment()
+ if config["mode"] == "off":
+ return
+ require_raw_sampling(turn["sampling"])
+ if turn["logprob_semantics"] != "raw_model" or turn["evidence_kind"] != "serving":
+ raise ValueError("Consistency requires observed raw serving evidence")
+ key = evidence_key(tokens, logprobs, mask)
+ version = str(turn["policy_version"])
+ if not version.isdecimal():
+ raise ValueError("Invalid serving version")
+ payload = {"key": key, "weight_version": version, "sampling": turn["sampling"],
+ "tokenizer_digest": turn["tokenizer_digest"], "template_digest": turn["template_digest"],
+ "logprob_semantics": "raw_model_equals_sampling", "evidence_kind": "serving"}
+ publish(Path(os.environ["RUN_DIR"]) / "consistency" / "turns" / version / f"{key}.json", payload)
+
+
+def identity_tis(*args, **kwargs):
+ raise RuntimeError("Consistency is metrics-only; invoking a TIS correction would change the Native objective")
+
+
+def check_training_batch(args, rollout_id, data, validator):
+ import torch.distributed as dist
+ from megatron.core import mpu
+ from slime.utils.distributed_utils import get_gloo_group
+
+ from harborrl.backends.slime_v032.versions import read_locked_policy_version
+
+ config = environment()
+ root = Path(os.environ["RUN_DIR"])
+ metadata = {"rank": dist.get_rank(), "rollout_id": rollout_id, "backend": "megatron",
+ "scope": "pre-update actual packed rollout batch"}
+ try:
+ validator(args, rollout_id, data)
+ if "log_probs" not in data or len(data["log_probs"]) != len(data["tokens"]):
+ raise ValueError("Missing pre-update Megatron log_probs; --get-mismatch-metrics is required")
+ version = read_locked_policy_version(root)
+ metadata["weight_version"] = version
+ cases = []
+ for tokens, q, mask, p0 in zip(data["tokens"], data["rollout_log_probs"], data["loss_masks"], data["log_probs"], strict=True):
+ tokens, q, mask, p0 = (x.detach().cpu().tolist() if hasattr(x, "detach") else list(x) for x in (tokens, q, mask, p0))
+ # Slime stores rollout probabilities as float32. Normalize the lookup identically.
+ key = evidence_key(tokens, q, mask)
+ evidence = read_json(root / "consistency" / "turns" / version / f"{key}.json")
+ if evidence["key"] != key or evidence["weight_version"] != version:
+ raise ValueError("Frozen serving evidence does not match the training batch")
+ for field, env in (("tokenizer_digest", "HARBORRL_NATIVE_TOKENIZER_DIGEST"),
+ ("template_digest", "HARBORRL_NATIVE_TEMPLATE_DIGEST")):
+ if evidence[field] != os.environ[env]:
+ raise ValueError(f"Training batch {field} differs from launch identity")
+ require_raw_sampling(evidence["sampling"])
+ cases.append((p0, q, mask))
+ part = assess(cases, config, metadata=metadata)
+ except (ValueError, TypeError, KeyError, OSError) as exc:
+ part = {"statistics": None, "error": str(exc), "metadata": metadata}
+ part["contribute"] = mpu.get_tensor_model_parallel_rank() == 0
+ distributed_gate(part, config, root / "consistency" / f"batch-{rollout_id}.json",
+ phase="pre-update-backend-gap", group=get_gloo_group())
diff --git a/harborrl/consistency/train.py b/harborrl/consistency/train.py
new file mode 100644
index 000000000..caaea02d9
--- /dev/null
+++ b/harborrl/consistency/train.py
@@ -0,0 +1,76 @@
+"""Run the locked upstream training loop with its supported actor-class factory."""
+import functools
+import importlib.util
+import json
+import os
+from pathlib import Path
+
+from .config import environment
+
+
+class CheckedActorGroup:
+ """Check every sync, including the final one, before returning to upstream."""
+ def __init__(self, actor, manager, args):
+ self.actor, self.manager, self.args = actor, manager, args
+
+ def __getattr__(self, name):
+ return getattr(self.actor, name)
+
+ def update_weights(self):
+ import ray
+
+ from harborrl.backends.slime_v032.rollout_service_client import (
+ fetch_pool_states,
+ )
+
+ from .sglang import admit_pool
+ result = self.actor.update_weights()
+ if self.args.offload_rollout:
+ ray.get(self.manager.onload_kv.remote())
+ endpoint = f"http://{self.args.sglang_router_ip}:{self.args.sglang_router_port}"
+ admit_pool(fetch_pool_states(endpoint), os.environ['RUN_DIR'])
+ return result
+
+
+def main():
+ import ray
+ from slime.ray.placement_group import create_training_models
+ from slime.utils.arguments import parse_args
+
+ from .actor import ConsistencyActor
+
+ args = parse_args()
+ for name, expected in (("pipeline_model_parallel_size", 1), ("context_parallel_size", 1),
+ ("update_weights_interval", 1)):
+ if getattr(args, name) != expected:
+ raise ValueError(f"Consistency v1 requires {name}={expected}")
+ if any(getattr(args, key, False) for key in ('use_critic', 'keep_old_actor', 'use_tis', 'release_train', 'use_rollout_routing_replay', 'num_experts')):
+ raise ValueError("Consistency v1 supports synchronous Native actor training without routing replay")
+ if args.update_weight_mode != "full" or args.update_weight_transport != "nccl":
+ raise ValueError("Consistency v1 requires full tensor/NCCL weight synchronization")
+ settings = environment()
+ rollout_env = {key: value for key, value in os.environ.items()
+ if key.startswith('HARBORRL_') or key in ('RUN_DIR', 'RUN_ID', 'MODEL_TAG', 'MODEL_ARGS_FILE',
+ 'ROLLOUT_MAX_RESPONSE_LEN', 'ROLLOUT_MAX_CONTEXT_LEN', 'PYTHONPATH')}
+ if not ray.is_initialized():
+ ray.init(runtime_env={'env_vars': rollout_env})
+ args.get_mismatch_metrics = True
+ args.custom_tis_function_path = "harborrl.consistency.slime.identity_tis"
+ args.train_env_vars = dict(args.train_env_vars or {})
+ args.train_env_vars.update({"HARBORRL_CONSISTENCY": json.dumps(settings), "RUN_DIR": os.environ["RUN_DIR"],
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": os.environ["HARBORRL_NATIVE_TOKENIZER_DIGEST"],
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": os.environ["HARBORRL_NATIVE_TEMPLATE_DIGEST"]})
+ spec = importlib.util.spec_from_file_location("harborrl_upstream_train", Path(os.environ["SLIME_DIR"]) / "train.py")
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ # Only the factory in this private entrypoint module is replaced; upstream files are untouched.
+ factory = functools.partial(create_training_models, actor_cls=ConsistencyActor)
+ def checked_factory(args, pgs, manager):
+ actor, critic = factory(args, pgs, manager)
+ return CheckedActorGroup(actor, manager, args), critic
+ module.create_training_models = checked_factory
+ module.train(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/harborrl/data/harbor/__main__.py b/harborrl/data/harbor/__main__.py
index 99d90d197..291463cbd 100644
--- a/harborrl/data/harbor/__main__.py
+++ b/harborrl/data/harbor/__main__.py
@@ -1,3 +1,4 @@
+"""Legacy TerminalEnv compatibility CLI; use harborrl hub inspect for native tasks."""
import argparse
import json
from pathlib import Path
diff --git a/harborrl/data/harbor/inspector.py b/harborrl/data/harbor/inspector.py
index 4a426fd4f..3499bdbd2 100644
--- a/harborrl/data/harbor/inspector.py
+++ b/harborrl/data/harbor/inspector.py
@@ -1,35 +1,16 @@
-"""Conservative inspection of pinned local Harbor tasks."""
+"""Legacy TerminalEnv compatibility tooling; native training does not use this path."""
-import hashlib
from dataclasses import asdict, dataclass
from pathlib import Path
-from .receipt import parse_reward
+from harborrl.tasks.content import tomllib, tree_digest # compatibility exports
-try:
- import tomllib
-except ModuleNotFoundError: # Python 3.10
- import tomli as tomllib
+from .receipt import parse_reward
PROFILE = "terminal_text_v1"
REWARD_PROFILE = "harbor_reward_txt_v1"
-def tree_digest(root: Path, *, exclude: tuple[str, ...] = ()) -> str:
- h = hashlib.sha256()
- for path in sorted(Path(root).rglob("*")):
- if str(path.relative_to(root)) in exclude:
- continue
- if path.is_symlink():
- raise ValueError("symlinks are not supported")
- if path.is_file():
- data = path.read_bytes()
- h.update(str(path.relative_to(root)).encode() + b"\0")
- h.update(str(path.stat().st_mode & 0o777).encode() + b"\0")
- h.update(str(len(data)).encode() + b"\0" + data)
- return h.hexdigest()
-
-
@dataclass(frozen=True)
class TaskRef:
dataset: str
diff --git a/harborrl/data/harbor/materializer.py b/harborrl/data/harbor/materializer.py
index 95346c414..ce0c59cec 100644
--- a/harborrl/data/harbor/materializer.py
+++ b/harborrl/data/harbor/materializer.py
@@ -1,4 +1,4 @@
-"""Publish a task without changing its verifier or Docker build context."""
+"""Legacy TerminalEnv compatibility tooling; native training does not use this path."""
import json
import shutil
diff --git a/harborrl/data/harbor/native_inspector.py b/harborrl/data/harbor/native_inspector.py
index de0a311b5..729f3c726 100644
--- a/harborrl/data/harbor/native_inspector.py
+++ b/harborrl/data/harbor/native_inspector.py
@@ -1,7 +1,7 @@
"""Native preflight: task metadata, not TerminalEnv materialization rules."""
from pathlib import Path
-from .inspector import tomllib, tree_digest
+from harborrl.tasks.content import tomllib, tree_digest
def inspect_native(path, *, expected_digest=None):
@@ -14,7 +14,7 @@ def inspect_native(path, *, expected_digest=None):
if expected_digest is not None and checksum != expected_digest:
raise ValueError("task content changed since catalog lock")
spec = tomllib.loads((root / "task.toml").read_text())
- for name in ("instruction.md", "environment/Dockerfile", "tests/test.sh"):
+ for name in ("instruction.md", "tests/test.sh"):
if not (root / name).is_file():
raise ValueError("missing " + name)
if not (root / "instruction.md").read_text().strip():
@@ -22,6 +22,8 @@ def inspect_native(path, *, expected_digest=None):
env, verifier = spec.get("environment", {}), spec.get("verifier", {})
if not isinstance(env, dict) or not isinstance(verifier, dict):
raise TypeError("invalid environment/verifier configuration")
+ if not (root / 'environment/Dockerfile').is_file() and not env.get('docker_image'):
+ raise ValueError('missing environment/Dockerfile or prebuilt docker_image')
if not isinstance(spec.get("agent", {}), dict):
raise TypeError("invalid agent configuration")
if spec.get("user_agent") or spec.get("bridge"):
diff --git a/harborrl/data/harbor/probe.py b/harborrl/data/harbor/probe.py
index 650941bb9..d3e11d948 100644
--- a/harborrl/data/harbor/probe.py
+++ b/harborrl/data/harbor/probe.py
@@ -1,11 +1,4 @@
-"""Probe a Harbor task with native Docker verifier semantics.
-
-The legacy TerminalEnv comparison path is intentionally not part of the public
-release. This probe runs the untouched task verifier twice in isolated
-containers and requires the same scalar reward, which keeps the check aligned
-with the Native Harbor Runner boundary without importing the old environment
-stack.
-"""
+"""Legacy TerminalEnv compatibility tooling; native training does not use this path."""
from __future__ import annotations
import asyncio
diff --git a/harborrl/data/harbor/receipt.py b/harborrl/data/harbor/receipt.py
index 425a8be4f..ad0476ab1 100644
--- a/harborrl/data/harbor/receipt.py
+++ b/harborrl/data/harbor/receipt.py
@@ -1,4 +1,4 @@
-"""Strict scalar receipt parsing: a missing/invalid score is an infra failure."""
+"""Legacy TerminalEnv compatibility tooling; native training does not use this path."""
import math
diff --git a/harborrl/deployment/__init__.py b/harborrl/deployment/__init__.py
new file mode 100644
index 000000000..cfae85518
--- /dev/null
+++ b/harborrl/deployment/__init__.py
@@ -0,0 +1 @@
+"""Deployment topology: machine roles, preflight and remote training targets."""
diff --git a/harborrl/deployment/controller.py b/harborrl/deployment/controller.py
new file mode 100644
index 000000000..0ee3700e3
--- /dev/null
+++ b/harborrl/deployment/controller.py
@@ -0,0 +1,207 @@
+"""Service-mode dispatch: the manager-side controller flow (P1 + P3).
+
+An independent CPU manager runs the same preflight graph, materializes the
+run on shared storage, starts the RolloutService control plane, launches the
+existing Slime job on the GPU target over SSH, and waits for the shared
+exit-code record. Rollout execution itself moves to the manager only through
+the service; training stays on the GPU.
+"""
+from __future__ import annotations
+
+import contextlib
+import json
+import os
+import signal
+import sys
+import threading
+from pathlib import Path
+
+from harborrl.config import ROOT
+from harborrl.deployment import remote
+from harborrl.deployment.schema import destination
+from harborrl.deployment.training_target import SshTrainingTarget
+from harborrl.platform.evidence import (
+ _materialize,
+ _source_audit,
+ effective_config,
+ new_run_root,
+ run_environment,
+)
+from harborrl.rollout.service import RolloutService, make_control_server
+from harborrl.trajectories.native import publish
+
+TOKEN_MODE = 0o600
+
+
+def check_gpu_control(deployment):
+ manager, gpu = deployment['manager'], deployment['gpu_target']
+ timeout = deployment['connectivity']['http_timeout_s']
+ result = remote.run_ssh(gpu['ssh'] or {'host': 'localhost'},
+ f"curl --noproxy '*' -fsS --max-time {timeout} " + remote.quote(manager['control_url'] + '/healthz'),
+ timeout=timeout + 15)
+ if result.returncode != 0:
+ raise RuntimeError('GPU cannot reach manager control service')
+ payload = json.loads(result.stdout)
+ if payload != {'ok': True, 'service': 'harborrl-rollout-service-v1'}:
+ raise RuntimeError('GPU reached an unexpected control service')
+ return {'gpu_to_controller': True, 'control_url': manager['control_url']}
+
+
+def _ensure_token(token_file):
+ """Load the run token, creating a fresh one on first use."""
+ path = Path(token_file)
+ if path.is_file():
+ token = path.read_text().strip()
+ if token:
+ return token
+ import secrets
+
+ token = secrets.token_urlsafe(32)
+ path.parent.mkdir(parents=True, exist_ok=True)
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, TOKEN_MODE)
+ with os.fdopen(fd, "w") as stream:
+ stream.write(token + "\n")
+ return token
+
+
+def _rollout_environment(plan, deployment, environment):
+ """Environment overlay that points the GPU job at the manager service."""
+ manager = deployment["manager"]
+ workers = deployment["rollout"]["workers"]
+ overlay = dict(environment)
+ overlay.update({
+ "HARBORRL_ROLLOUT_MODE": "service",
+ "HARBORRL_ROLLOUT_SERVICE_URL": manager["control_url"],
+ "HARBORRL_ROLLOUT_SERVICE_TOKEN_FILE": manager['run_token_file'],
+ "TRAIN_PYTHON": deployment['gpu_target']['train_python'],
+ "HARBORRL_NATIVE_GATEWAY_HOST": manager["bind"],
+ "HARBORRL_NATIVE_GATEWAY_PORT": str(manager["gateway_port"]),
+ "HARBORRL_NATIVE_GATEWAY_URL": manager["gateway_url"],
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(
+ [destination(worker["ssh"]) for worker in workers]),
+ "HARBORRL_NATIVE_RUNNER_PYTHON": (plan['config']['harbor']['python']
+ if deployment['rollout']['provider'] == 'sandbox'
+ else workers[0]['runner_python']),
+ })
+ if deployment['rollout']['provider'] == 'sandbox':
+ if 'HARBORRL_NATIVE_SANDBOX' not in overlay:
+ raise ValueError('sandbox deployment requires sandbox training config')
+ transport = plan['config']['harbor'].get('runner_transport', 'ssh')
+ if (transport == 'local' and workers) or (transport == 'ssh' and not workers):
+ raise ValueError('sandbox runner transport and deployment workers disagree')
+ overlay['HARBORRL_NATIVE_RUNNER_TRANSPORT'] = transport
+ return overlay
+
+
+def _runtime_factory(deployment, environment, *, concurrency_limit, group_size, tokenizer=None):
+ """Build the per-run rollout runtime when the GPU job announces serving."""
+
+ def factory(run_id, router_url):
+ from harborrl.rollout.runtime import RolloutRuntime, RolloutRuntimeConfig
+
+ env = {**os.environ, **environment}
+ config = RolloutRuntimeConfig.from_environment(
+ env, endpoint=router_url, concurrency_limit=concurrency_limit,
+ group_size=group_size,
+ gateway_host=deployment["manager"]["bind"],
+ gateway_port=deployment["manager"]["gateway_port"],
+ gateway_url=deployment["manager"]["gateway_url"])
+ resolved = tokenizer
+ if resolved is None:
+ from transformers import AutoTokenizer
+
+ resolved = AutoTokenizer.from_pretrained(
+ env["HF_CKPT"], trust_remote_code=True)
+ return RolloutRuntime(config, resolved)
+
+ return factory
+
+
+def dispatch_service(plan, deployment, *, checks):
+ manager, gpu, storage = deployment["manager"], deployment["gpu_target"], deployment["storage"]
+ config = plan["config"]
+ root = new_run_root(storage["run_root"])
+ root.mkdir(parents=True, mode=0o700)
+ run_id = root.name
+
+ catalog_path, prompt_path = _materialize(plan, root)
+ source = _source_audit(root)
+ environment = run_environment(plan, root, catalog_path, prompt_path)
+ token = _ensure_token(manager["run_token_file"])
+ environment = _rollout_environment(plan, deployment, environment)
+
+ sampling = config["sampling"]
+ concurrency = sampling["group_size"] * sampling["groups_per_batch"]
+ service = RolloutService(
+ run_root=root,
+ runtime_factory=_runtime_factory(
+ deployment, environment,
+ concurrency_limit=concurrency, group_size=sampling["group_size"]),
+ token_file=manager["run_token_file"])
+ control = make_control_server(service, host=manager["bind"],
+ port=manager["control_port"])
+ control_thread = None
+ target = None
+ exit_code = 1
+ try:
+ service.register_run(run_id, {
+ "run_id": run_id, "deployment_source": plan["deployment"]["source"],
+ "source_commit": source["commit"],
+ "config_digest": plan["deployment"]["digest"],
+ "group_size": sampling["group_size"],
+ "workers": [worker["id"] for worker in deployment["rollout"]["workers"]],
+ }, token=token)
+ control_thread = threading.Thread(
+ target=control.serve_forever, name="harborrl-control-plane", daemon=True)
+ control_thread.start()
+ publish(root / 'control-readiness.json', check_gpu_control(deployment))
+
+ publish(root / "launch.json", {
+ "plan": plan, "run_dir": str(root), "source": source,
+ "materialized_catalog": str(catalog_path), "prompt_data": str(prompt_path),
+ "control_url": manager["control_url"]})
+ publish(root / "effective-config.json",
+ effective_config(plan, environment, dict(environment), source, checks))
+
+ target = SshTrainingTarget(
+ gpu["ssh"] or {"host": "localhost"}, source_dir=gpu["source_dir"], run_dir=root,
+ environment_file=gpu.get("environment_file"),
+ command_timeout=deployment["connectivity"]["command_timeout_s"] + 30,
+ ssh_timeout=deployment["connectivity"]["ssh_timeout_s"])
+ # The launcher executes inside the GPU checkout; manager-side absolute
+ # paths are rewritten so shared-storage artifacts keep one spelling.
+ target.write_launcher(
+ ["bash", str(ROOT / "harborrl/platform/slime_train.sh")], environment,
+ path_rewrites=[(str(manager["source_dir"]), str(gpu["source_dir"]))])
+ target.launch()
+
+ def forward_signal(signum, frame):
+ if target is not None:
+ target.cancel()
+
+ previous = {sig: signal.signal(sig, forward_signal)
+ for sig in (signal.SIGINT, signal.SIGTERM)}
+ try:
+ exit_code = target.wait()
+ finally:
+ for sig, handler in previous.items():
+ signal.signal(sig, handler)
+ except BaseException:
+ import traceback
+
+ traceback.print_exc()
+ if target is not None:
+ # Best-effort teardown; the run record keeps the failure evidence.
+ with contextlib.suppress(Exception):
+ target.cancel()
+ raise
+ finally:
+ with contextlib.suppress(Exception):
+ service.drain_run(run_id)
+ service.close()
+ if control_thread is not None:
+ control.shutdown()
+ control_thread.join(5)
+ control.server_close()
+ print(json.dumps({"run_dir": str(root), "exit_code": exit_code}), file=sys.stderr)
+ return exit_code
diff --git a/harborrl/deployment/preflight.py b/harborrl/deployment/preflight.py
new file mode 100644
index 000000000..94cb6fd6f
--- /dev/null
+++ b/harborrl/deployment/preflight.py
@@ -0,0 +1,340 @@
+"""Role-layered preflight graph shared by train, doctor and quickstart.
+
+Every check names the role and machine it ran against, the outcome, and a
+user-executable fix. The graph never raises: failures are data, and any
+failed mandatory check keeps a run from being created.
+"""
+from __future__ import annotations
+
+import json
+import socket
+import subprocess
+import time
+from pathlib import Path
+from urllib.parse import urlsplit
+
+from harborrl.deployment import remote
+from harborrl.deployment.schema import destination
+
+
+def _check(role, machine, name, ok, detail="", hint=None, duration_ms=None):
+ record = {"role": role, "machine": machine, "check": name, "ok": bool(ok),
+ "detail": str(detail)}
+ if hint:
+ record["hint"] = str(hint)
+ if duration_ms is not None:
+ record["duration_ms"] = round(duration_ms, 3)
+ return record
+
+
+def _timed(record_factory):
+ started = time.monotonic()
+ record = record_factory()
+ record["duration_ms"] = (time.monotonic() - started) * 1000
+ return record
+
+
+def _bindable(host, port):
+ """Probe with the same address reuse semantics as our HTTP servers."""
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
+ try:
+ server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ server.bind((host, port))
+ server.listen()
+ return True, "port is free"
+ except OSError as exc:
+ return False, f"cannot bind manager listener on {host}:{port}: {exc}"
+
+
+def _manager_checks(deployment):
+ manager, storage = deployment["manager"], deployment["storage"]
+ checks = []
+ run_root = Path(storage["run_root"])
+ exists = run_root.is_dir()
+ writable = exists and _touch_ok(run_root) if storage["manager_writable"] else exists
+ checks.append(_timed(lambda: _check(
+ "manager", manager["id"], "storage.run_root", exists and writable, str(run_root),
+ hint="create the shared run root and grant the manager write access")))
+
+ if manager["mode"] == "service":
+ token = Path(manager["run_token_file"])
+ token_ok = token.is_file()
+ detail = str(token)
+ if token_ok:
+ mode = token.stat().st_mode & 0o777
+ token_ok = mode & 0o077 == 0
+ detail = f"{token} mode {oct(mode)}"
+ checks.append(_timed(lambda: _check(
+ "manager", manager["id"], "manager.run_token_file", token_ok, detail,
+ hint="install the run-scoped token with chmod 600")))
+
+ for name, port in (("manager.control_port", manager["control_port"]),
+ ("manager.gateway_port", manager["gateway_port"])):
+ started = time.monotonic()
+ ok, detail = _bindable(manager["bind"], port)
+ checks.append(_check(
+ "manager", manager["id"], name, ok, detail,
+ hint=f"free the port or stop the conflicting listener on {manager['bind']}:{port}",
+ duration_ms=(time.monotonic() - started) * 1000))
+ return checks
+
+
+def _touch_ok(directory):
+ probe = directory / ".harborrl-preflight-probe"
+ try:
+ probe.write_text("write probe\n")
+ probe.unlink()
+ return True
+ except OSError:
+ return False
+
+
+def _local_runner():
+ def run(command, *, timeout=30):
+ try:
+ return subprocess.run(command, capture_output=True, text=True,
+ timeout=timeout, check=False, shell=isinstance(command, str))
+ except (subprocess.TimeoutExpired, OSError) as exc:
+ return subprocess.CompletedProcess(command, 255, "", f"{type(exc).__name__}: {exc}")
+
+ def test(expression):
+ result = run(f"test {expression} && echo ok")
+ return result.returncode == 0 and result.stdout.strip() == "ok", result
+
+ return run, test
+
+
+def _gpu_checks(plan, deployment):
+ gpu, connectivity = deployment["gpu_target"], deployment["connectivity"]
+ config = plan["config"]
+ machine = gpu["id"]
+ checks = []
+ if gpu["transport"] == "ssh":
+ spec = gpu["ssh"]
+ started = time.monotonic()
+ probe = remote.run_ssh(spec, "true", timeout=connectivity["ssh_timeout_s"] + 5,
+ connect_timeout=connectivity["ssh_timeout_s"])
+ checks.append(_check(
+ "gpu_target", machine, "gpu_target.ssh", probe.returncode == 0,
+ probe.stderr.strip()[-300:] or "non-interactive ssh ok",
+ hint="the SSH config alias, agent or identity file must allow non-interactive login",
+ duration_ms=(time.monotonic() - started) * 1000))
+ if probe.returncode != 0:
+ checks.append(_check(
+ "gpu_target", machine, "gpu_target.further_probes", True,
+ "skipped: fix gpu_target.ssh first, then rerun for path, model and GPU checks"))
+ return checks
+
+ def execute(command):
+ return remote.run_ssh(spec, command, timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+
+ def shell_test(expression):
+ ok, result = remote.test_remote(spec, expression,
+ timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+ return ok, result
+ else:
+ run, test = _local_runner()
+
+ def execute(command):
+ return run(command, timeout=connectivity["command_timeout_s"])
+
+ def shell_test(expression):
+ return test(expression)
+
+ def path_check(name, expression, target, hint):
+ started = time.monotonic()
+ ok, _result = shell_test(expression)
+ checks.append(_check("gpu_target", machine, name, ok, target,
+ hint=hint, duration_ms=(time.monotonic() - started) * 1000))
+
+ path_check("gpu_target.source_dir", f"-d {remote.quote(gpu['source_dir'])}", gpu["source_dir"],
+ "mount or clone the HarborRL checkout at gpu_target.source_dir")
+ path_check("gpu_target.train_python", f"-x {remote.quote(gpu['train_python'])}", gpu["train_python"],
+ "provision the training interpreter at gpu_target.train_python")
+ if gpu.get("environment_file"):
+ path_check("gpu_target.environment_file",
+ f"-f {remote.quote(gpu['environment_file'])}", gpu["environment_file"],
+ "provide the controlled environment variable map")
+ for key in ("checkpoint", "reference"):
+ target = config["model"][key]
+ path_check(f"model.{key}", f"-d {remote.quote(target)}", target,
+ "the model checkpoint must exist in GPU-machine path semantics")
+
+ started = time.monotonic()
+ gpu_list = execute("nvidia-smi --query-gpu=index --format=csv,noheader")
+ visible = len([line for line in gpu_list.stdout.splitlines() if line.strip()])
+ checks.append(_check(
+ "gpu_target", machine, "gpu_target.expected_gpus",
+ gpu_list.returncode == 0 and visible >= gpu["expected_gpus"],
+ f"{visible} visible GPUs (returncode {gpu_list.returncode})",
+ hint="check CUDA visibility and the GPU allocation before training",
+ duration_ms=(time.monotonic() - started) * 1000))
+ return checks
+
+
+def _worker_checks(plan, deployment):
+ workers, connectivity = deployment["rollout"]["workers"], deployment["connectivity"]
+ expected_harbor = plan["config"]["harbor"]["version"]
+ checks = []
+ for worker in workers:
+ spec, machine = worker["ssh"], worker["id"]
+ started = time.monotonic()
+ probe = remote.run_ssh(spec, "true", timeout=connectivity["ssh_timeout_s"] + 5,
+ connect_timeout=connectivity["ssh_timeout_s"])
+ checks.append(_check(
+ "rollout_worker", machine, "worker.ssh", probe.returncode == 0,
+ probe.stderr.strip()[-300:] or "non-interactive ssh ok",
+ hint=f"SSH access to {destination(spec)} must work without a password prompt",
+ duration_ms=(time.monotonic() - started) * 1000))
+ if probe.returncode != 0:
+ continue
+
+ def worker_test(spec, machine, name, expression, target, hint):
+ started = time.monotonic()
+ ok, _result = remote.test_remote(spec, expression,
+ timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+ checks.append(_check("rollout_worker", machine, name, ok, target,
+ hint=hint, duration_ms=(time.monotonic() - started) * 1000))
+
+ worker_test(spec, machine, "worker.source_dir",
+ f"-d {remote.quote(worker['source_dir'])}",
+ worker["source_dir"], "mount the HarborRL checkout on the worker")
+ worker_test(spec, machine, "worker.runner_python",
+ f"-f {remote.quote(worker['runner_python'])}",
+ worker["runner_python"], "provision the runner interpreter on the worker")
+ for index, task_root in enumerate(worker["task_roots"]):
+ worker_test(spec, machine, f"worker.task_roots[{index}]",
+ f"-d {remote.quote(task_root)}",
+ task_root, "mount the task tree on the worker")
+
+ started = time.monotonic()
+ probe_command = (f"cd {remote.quote(worker['source_dir'])} && "
+ f"{remote.quote(worker['runner_python'])} -m "
+ "harborrl.rollout.harbor_job.runner --probe")
+ result = remote.run_ssh(spec, probe_command, timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+ runtime_ok = harbor_ok = False
+ detail = result.stderr.strip()[-300:] or result.stdout.strip()[-300:]
+ try:
+ payload = json.loads(result.stdout)
+ parts = payload.get("python", "").split(".")[:2]
+ runtime_ok = (result.returncode == 0
+ and len(parts) == 2 and all(p.isdigit() for p in parts)
+ and tuple(map(int, parts)) >= (3, 12))
+ harbor_ok = payload.get("harbor_version") == expected_harbor
+ if result.returncode == 0:
+ detail = f"python {payload.get('python')}, harbor {payload.get('harbor_version')}"
+ except (ValueError, AttributeError, TypeError):
+ pass
+ checks.append(_check(
+ "rollout_worker", machine, "worker.runner_runtime", runtime_ok, detail,
+ hint="runner Python must be >= 3.12 with the pinned Harbor installation",
+ duration_ms=(time.monotonic() - started) * 1000))
+ checks.append(_check(
+ "rollout_worker", machine, "worker.harbor_version", harbor_ok, detail,
+ hint=f"pin Harbor {expected_harbor} in the runner environment",
+ duration_ms=(time.monotonic() - started) * 1000))
+
+ started = time.monotonic()
+ docker = remote.run_ssh(spec, "docker info --format '{{.ServerVersion}}'",
+ timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+ checks.append(_check(
+ "rollout_worker", machine, "worker.docker", docker.returncode == 0,
+ docker.stdout.strip()[:120] or docker.stderr.strip()[-200:],
+ hint="start the Docker daemon; Harbor trials require container execution",
+ duration_ms=(time.monotonic() - started) * 1000))
+ return checks
+
+
+def _control_loop_checks(deployment):
+ manager, connectivity = deployment["manager"], deployment["connectivity"]
+ checks = []
+ if manager["mode"] != "service":
+ return checks
+ dials = (("connectivity.gpu_to_controller", manager["control_url"], connectivity["gpu_to_controller"]),
+ ("connectivity.worker_to_gateway", manager["gateway_url"], connectivity["worker_to_gateway"]))
+ for name, url, enabled in dials:
+ if not enabled:
+ continue
+ parts = urlsplit(url)
+ started = time.monotonic()
+ try:
+ socket.getaddrinfo(parts.hostname, parts.port)
+ ok, detail = True, "address resolves from the manager"
+ except socket.gaierror as exc:
+ ok, detail = False, f"{exc}"
+ checks.append(_check("control_loop", manager["id"], name, ok, detail,
+ hint="fix DNS or firewall so the address resolves for every role",
+ duration_ms=(time.monotonic() - started) * 1000))
+ started = time.monotonic()
+ serving = False
+ try:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
+ client.settimeout(min(connectivity["http_timeout_s"], 5))
+ serving = client.connect_ex((parts.hostname, parts.port)) == 0
+ except OSError:
+ serving = False # unreachable now; the post-start dial retries
+ checks.append(_check(
+ "control_loop", manager["id"], name + ".dial", True,
+ "listener already serving" if serving
+ else "no listener yet; the live dial reruns after components start",
+ duration_ms=(time.monotonic() - started) * 1000))
+ if connectivity["manager_to_gpu_inference"]:
+ checks.append(_check(
+ "control_loop", manager["id"], "connectivity.manager_to_gpu_inference", True,
+ "deferred: the GPU job publishes its inference endpoint after serving is healthy"))
+ return checks
+
+
+def preflight(plan, deployment, *, live=True):
+ """Run every deployment layer; remote probes are skipped when live=False."""
+ if not live:
+ return [_check("deployment", deployment["manager"]["id"], "deployment.config",
+ True, "dry-run: parsed and validated, remote probes skipped")]
+ checks = []
+ checks.extend(_manager_checks(deployment))
+ checks.extend(_gpu_checks(plan, deployment))
+ if deployment["rollout"]["provider"] == "ssh_docker":
+ checks.extend(_worker_checks(plan, deployment))
+ checks.extend(_control_loop_checks(deployment))
+ return checks
+
+
+def readiness_checks(plan, deployment, *, from_specs=()):
+ """Post-start dials: cross-machine health after components are listening.
+
+ ``from_specs`` maps a role to the ssh spec that should perform the dial;
+ callers pass the GPU target and rollout workers of a live deployment.
+ """
+ manager, connectivity = deployment["manager"], deployment["connectivity"]
+ checks = []
+ dials = (("gpu_target", "gpu_to_controller.ready", manager["control_url"],
+ connectivity["gpu_to_controller"]),
+ ("rollout_worker", "worker_to_gateway.ready", manager["gateway_url"],
+ connectivity["worker_to_gateway"]))
+ specs = dict(from_specs)
+ for role, name, url, enabled in dials:
+ if not enabled:
+ continue
+ spec = specs.get(role)
+ if spec is None:
+ checks.append(_check("control_loop", manager["id"], name, True,
+ "deferred: no machine mapped for this dial"))
+ continue
+ path = "/healthz" if "control" in name else "/readyz"
+ command = (f"python3 -c "
+ f"\"import urllib.request;urllib.request.urlopen('{url}{path}',"
+ f"timeout={connectivity['http_timeout_s']})\"")
+ started = time.monotonic()
+ result = remote.run_ssh(spec, command, timeout=connectivity["command_timeout_s"],
+ connect_timeout=connectivity["ssh_timeout_s"])
+ checks.append(_check("control_loop", destination(spec), name,
+ result.returncode == 0,
+ result.stderr.strip()[-300:] or "health dial ok",
+ hint=f"open the route from {destination(spec)} to {url}{path}",
+ duration_ms=(time.monotonic() - started) * 1000))
+ return checks
diff --git a/harborrl/deployment/remote.py b/harborrl/deployment/remote.py
new file mode 100644
index 000000000..e9ddd276c
--- /dev/null
+++ b/harborrl/deployment/remote.py
@@ -0,0 +1,55 @@
+"""Non-interactive SSH transport for deployment preflight and launch.
+
+Every invocation runs BatchMode: a password prompt is a failed check, never an
+interactive hang. User, port and identity are inherited from the invoking
+user's SSH config unless the deployment spec narrows them.
+"""
+from __future__ import annotations
+
+import shlex
+import subprocess
+
+SSH_OPTIONS = ("BatchMode=yes", "ClearAllForwardings=yes", "StrictHostKeyChecking=accept-new")
+
+
+def ssh_command(spec, remote_command, *, connect_timeout=15):
+ """Assemble one non-interactive ssh argv for a normalized spec."""
+ argv = ["ssh"]
+ for option in SSH_OPTIONS:
+ argv += ["-o", option]
+ argv += ["-o", f"ConnectTimeout={int(connect_timeout)}"]
+ if spec.get("port"):
+ argv += ["-p", str(spec["port"])]
+ if spec.get("identity"):
+ argv += ["-i", str(spec["identity"])]
+ from harborrl.deployment.schema import destination
+
+ argv += [destination(spec), remote_command]
+ return argv
+
+
+def run_ssh(spec, command, *, timeout=30, connect_timeout=15):
+ """Run one remote command; transport failures surface as returncode 255."""
+ argv = ssh_command(spec, command, connect_timeout=connect_timeout)
+ try:
+ return subprocess.run(argv, capture_output=True, text=True,
+ timeout=timeout, check=False)
+ except (subprocess.TimeoutExpired, OSError) as exc:
+ return subprocess.CompletedProcess(argv, 255, "", f"{type(exc).__name__}: {exc}")
+
+
+def test_remote(spec, expression, *, timeout=30, connect_timeout=15):
+ """Evaluate a shell test expression on the target; True when it passes."""
+ result = run_ssh(spec, f"test {expression} && echo ok", timeout=timeout,
+ connect_timeout=connect_timeout)
+ return result.returncode == 0 and result.stdout.strip() == "ok", result
+
+
+def remote_read(spec, command, *, timeout=30, connect_timeout=15):
+ """Return (returncode, stdout, stderr) for a remote command."""
+ result = run_ssh(spec, command, timeout=timeout, connect_timeout=connect_timeout)
+ return result.returncode, result.stdout, result.stderr
+
+
+def quote(value):
+ return shlex.quote(str(value))
diff --git a/harborrl/deployment/schema.py b/harborrl/deployment/schema.py
new file mode 100644
index 000000000..3cc7bf7c8
--- /dev/null
+++ b/harborrl/deployment/schema.py
@@ -0,0 +1,330 @@
+"""Deployment schema v1: machine roles and access, never training semantics.
+
+A deployment config describes WHERE the manager, GPU trainer and rollout
+workers live and HOW they reach each other. It deliberately excludes every
+training-side field (learning rate, sampling, task catalog) and never stores
+secrets: SSH targets must resolve through the invoking user's SSH config,
+agent or a restricted identity file.
+"""
+from __future__ import annotations
+
+import re
+import sys
+from pathlib import Path
+from urllib.parse import urlsplit
+
+import yaml
+
+from harborrl.config import ROOT
+
+DEPLOYMENT_SCHEMA_VERSION = 1
+DEPLOYMENT_SECTIONS = {"manager", "gpu_target", "storage", "rollout", "connectivity"}
+
+SLUG = re.compile(r"[A-Za-z0-9][A-Za-z0-9_-]*")
+LOOPBACK_HOSTS = {"localhost", "127.0.0.1", "::1", "0.0.0.0", "[::1]"}
+
+MANAGER_FIELDS = {"id", "mode", "bind", "control_port", "control_url", "gateway_port",
+ "gateway_url", "run_token_file", "source_dir", "controller_python"}
+SSH_FIELDS = {"host", "user", "port", "identity"}
+GPU_FIELDS = {"id", "transport", "ssh", "source_dir", "train_python", "environment_file", "expected_gpus"}
+STORAGE_FIELDS = {"mode", "run_root", "manager_writable", "gpu_writable", "worker_writable"}
+WORKER_FIELDS = {"id", "ssh", "source_dir", "runner_python", "docker", "task_roots"}
+ROLLOUT_FIELDS = {"provider", "workers"}
+CONNECTIVITY_FIELDS = {"command_timeout_s", "ssh_timeout_s", "http_timeout_s",
+ "gpu_to_controller", "worker_to_gateway", "manager_to_gpu_inference"}
+TRANSPORTS = ("local", "ssh")
+ROLLOUT_PROVIDERS = ("ssh_docker", "embedded", "sandbox")
+STORAGE_MODES = ("shared_fs",)
+
+
+class DeploymentError(ValueError):
+ """Raised for any malformed or unsafe deployment configuration."""
+
+
+def _slug(value, label):
+ if not isinstance(value, str) or not SLUG.fullmatch(value):
+ raise DeploymentError(f"{label} must be a short machine identifier")
+ return value
+
+
+def _positive_int(value, label):
+ if type(value) is not int or value <= 0 or type(value) is bool:
+ raise DeploymentError(f"{label} must be a positive integer")
+ return value
+
+
+def _nonempty_str(value, label):
+ if not isinstance(value, str) or not value.strip():
+ raise DeploymentError(f"{label} must be a nonempty string")
+ return value
+
+
+def _bool(value, label):
+ if type(value) is not bool:
+ raise DeploymentError(f"{label} must be a boolean")
+ return value
+
+
+def _absolute_path(value, label):
+ value = _nonempty_str(value, label)
+ if not Path(value).is_absolute():
+ raise DeploymentError(f"{label} must be an absolute path in target-machine semantics")
+ return value
+
+
+def _plain_origin(url, label, *, port=None, allow_loopback=False):
+ parts = urlsplit(_nonempty_str(url, label))
+ if (parts.scheme not in ("http", "https") or not parts.hostname
+ or parts.username or parts.password or parts.query or parts.fragment
+ or parts.path not in ("", "/")
+ or (port is not None and parts.port != port)):
+ raise DeploymentError(f"{label} must be a plain http(s) origin matching its declared port")
+ if not allow_loopback and parts.hostname.lower() in LOOPBACK_HOSTS:
+ raise DeploymentError(f"{label} must be reachable from other machines; loopback is forbidden")
+ return f"{parts.scheme}://{parts.netloc}".rstrip("/")
+
+
+def _ssh_spec(value, label, *, require_host=True):
+ if value is None:
+ value = {}
+ if not isinstance(value, dict) or set(value) - SSH_FIELDS:
+ raise DeploymentError(
+ f"{label} supports only {sorted(SSH_FIELDS)}; passwords and private keys are never stored")
+ host = value.get("host")
+ if host is not None:
+ host = _nonempty_str(host, f"{label}.host")
+ if host.startswith("-") or host.count("@") > 1:
+ raise DeploymentError(f"{label}.host must be an SSH destination, not a flag")
+ if require_host and not host:
+ raise DeploymentError(f"{label}.host is required")
+ user = value.get("user")
+ if user is not None:
+ user = _nonempty_str(user, f"{label}.user")
+ if user.startswith("-"):
+ raise DeploymentError(f"{label}.user must not look like a flag")
+ port = value.get("port")
+ if port is not None:
+ port = _positive_int(port, f"{label}.port")
+ identity = value.get("identity")
+ if identity is not None:
+ identity = _absolute_path(identity, f"{label}.identity")
+ spec = {"host": host}
+ if user:
+ spec["user"] = user
+ if port:
+ spec["port"] = port
+ if identity:
+ spec["identity"] = identity
+ return spec
+
+
+def destination(spec):
+ """Render an ssh destination from a normalized spec (user@host)."""
+ return f"{spec['user']}@{spec['host']}" if spec.get("user") else spec["host"]
+
+
+def _manager(config):
+ if not isinstance(config, dict) or set(config) != MANAGER_FIELDS:
+ raise DeploymentError(f"manager requires exactly {sorted(MANAGER_FIELDS)}")
+ mode = config["mode"]
+ if mode not in ("embedded", "service"):
+ raise DeploymentError("manager.mode must be embedded or service")
+ service = mode == "service"
+ control_port = _positive_int(config["control_port"], "manager.control_port")
+ gateway_port = _positive_int(config["gateway_port"], "manager.gateway_port")
+ if control_port == gateway_port:
+ raise DeploymentError("manager.control_port and manager.gateway_port must differ")
+ if not 0 < control_port < 65536 or not 0 < gateway_port < 65536:
+ raise DeploymentError("manager ports must be in 1..65535")
+ control_url = _plain_origin(config["control_url"], "manager.control_url",
+ port=control_port, allow_loopback=not service)
+ gateway_url = _plain_origin(config["gateway_url"], "manager.gateway_url",
+ port=gateway_port, allow_loopback=not service)
+ return {
+ "id": _slug(config["id"], "manager.id"), "mode": mode,
+ "bind": _nonempty_str(config["bind"], "manager.bind"),
+ "control_port": control_port, "control_url": control_url,
+ "gateway_port": gateway_port, "gateway_url": gateway_url,
+ "run_token_file": _absolute_path(config["run_token_file"], "manager.run_token_file"),
+ "source_dir": _absolute_path(config["source_dir"], "manager.source_dir"),
+ "controller_python": _absolute_path(config["controller_python"], "manager.controller_python"),
+ }
+
+
+def _gpu_target(config):
+ if not isinstance(config, dict) or set(config) != GPU_FIELDS:
+ raise DeploymentError(f"gpu_target requires exactly {sorted(GPU_FIELDS)}")
+ transport = config["transport"]
+ if transport not in TRANSPORTS:
+ raise DeploymentError(f"gpu_target.transport must be one of {TRANSPORTS}")
+ ssh = config["ssh"]
+ if transport == "ssh":
+ ssh = _ssh_spec(ssh, "gpu_target.ssh")
+ elif ssh is not None:
+ raise DeploymentError("gpu_target.ssh must be null for local transport")
+ target = {
+ "id": _slug(config["id"], "gpu_target.id"), "transport": transport, "ssh": ssh,
+ "source_dir": _absolute_path(config["source_dir"], "gpu_target.source_dir"),
+ "train_python": _absolute_path(config["train_python"], "gpu_target.train_python"),
+ "expected_gpus": _positive_int(config["expected_gpus"], "gpu_target.expected_gpus"),
+ }
+ environment_file = config["environment_file"]
+ if environment_file is not None:
+ target["environment_file"] = _absolute_path(environment_file, "gpu_target.environment_file")
+ return target
+
+
+def _storage(config):
+ if not isinstance(config, dict) or set(config) != STORAGE_FIELDS:
+ raise DeploymentError(f"storage requires exactly {sorted(STORAGE_FIELDS)}")
+ if config["mode"] not in STORAGE_MODES:
+ raise DeploymentError(f"storage.mode must be one of {STORAGE_MODES}")
+ return {
+ "mode": config["mode"],
+ "run_root": _absolute_path(config["run_root"], "storage.run_root"),
+ "manager_writable": _bool(config["manager_writable"], "storage.manager_writable"),
+ "gpu_writable": _bool(config["gpu_writable"], "storage.gpu_writable"),
+ "worker_writable": _bool(config["worker_writable"], "storage.worker_writable"),
+ }
+
+
+def _rollout(config):
+ if not isinstance(config, dict) or set(config) != ROLLOUT_FIELDS:
+ raise DeploymentError(f"rollout requires exactly {sorted(ROLLOUT_FIELDS)}")
+ if config["provider"] not in ROLLOUT_PROVIDERS:
+ raise DeploymentError(f"rollout.provider must be one of {ROLLOUT_PROVIDERS}")
+ workers = config["workers"]
+ # ssh_docker needs explicit machines; the embedded provider falls back to
+ # the training config's worker list, so it may start empty.
+ if (not isinstance(workers, list)
+ or (not workers and config["provider"] not in ("embedded", "sandbox"))):
+ raise DeploymentError("rollout.workers must be a nonempty list")
+ normalized, seen = [], set()
+ for worker in workers:
+ if not isinstance(worker, dict) or set(worker) != WORKER_FIELDS:
+ raise DeploymentError(f"rollout worker requires exactly {sorted(WORKER_FIELDS)}")
+ worker_id = _slug(worker["id"], "rollout worker id")
+ if worker_id in seen:
+ raise DeploymentError(f"duplicate rollout worker id: {worker_id}")
+ seen.add(worker_id)
+ task_roots = worker["task_roots"]
+ if (not isinstance(task_roots, list) or not task_roots
+ or any(not isinstance(root, str) or not root.strip() for root in task_roots)):
+ raise DeploymentError(f"worker {worker_id} requires nonempty task_roots")
+ normalized.append({
+ "id": worker_id,
+ "ssh": _ssh_spec(worker["ssh"], f"rollout worker {worker_id} ssh"),
+ "source_dir": _absolute_path(worker["source_dir"], f"worker {worker_id} source_dir"),
+ "runner_python": _absolute_path(worker["runner_python"], f"worker {worker_id} runner_python"),
+ "docker": _bool(worker["docker"], f"worker {worker_id} docker"),
+ "task_roots": [_absolute_path(root, f"worker {worker_id} task_root") for root in task_roots],
+ })
+ return {"provider": config["provider"], "workers": normalized}
+
+
+def _connectivity(config):
+ if not isinstance(config, dict) or set(config) != CONNECTIVITY_FIELDS:
+ raise DeploymentError(f"connectivity requires exactly {sorted(CONNECTIVITY_FIELDS)}")
+ return {
+ "command_timeout_s": _positive_int(config["command_timeout_s"], "connectivity.command_timeout_s"),
+ "ssh_timeout_s": _positive_int(config["ssh_timeout_s"], "connectivity.ssh_timeout_s"),
+ "http_timeout_s": _positive_int(config["http_timeout_s"], "connectivity.http_timeout_s"),
+ "gpu_to_controller": _bool(config["gpu_to_controller"], "connectivity.gpu_to_controller"),
+ "worker_to_gateway": _bool(config["worker_to_gateway"], "connectivity.worker_to_gateway"),
+ "manager_to_gpu_inference": _bool(config["manager_to_gpu_inference"], "connectivity.manager_to_gpu_inference"),
+ }
+
+
+def validate(config):
+ """Strictly validate and normalize a deployment mapping."""
+ if not isinstance(config, dict):
+ raise DeploymentError("deployment config must be a mapping")
+ if set(config) != DEPLOYMENT_SECTIONS | {"schema_version"}:
+ raise DeploymentError(
+ "deployment config requires exactly schema_version and "
+ + ", ".join(sorted(DEPLOYMENT_SECTIONS)))
+ if config["schema_version"] != DEPLOYMENT_SCHEMA_VERSION or type(config["schema_version"]) is not int:
+ raise DeploymentError(f"deployment schema_version must be {DEPLOYMENT_SCHEMA_VERSION}")
+ return {
+ "schema_version": DEPLOYMENT_SCHEMA_VERSION,
+ "manager": _manager(config["manager"]),
+ "gpu_target": _gpu_target(config["gpu_target"]),
+ "storage": _storage(config["storage"]),
+ "rollout": _rollout(config["rollout"]),
+ "connectivity": _connectivity(config["connectivity"]),
+ }
+
+
+def embedded_local(*, run_root=None, workers=(), expected_gpus=1):
+ """Default same-machine deployment; behavior matches the legacy pipeline."""
+ return validate({
+ "schema_version": 1,
+ "manager": {"id": "embedded-local", "mode": "embedded", "bind": "127.0.0.1",
+ "control_port": 49200, "control_url": "http://127.0.0.1:49200",
+ "gateway_port": 49184, "gateway_url": "http://127.0.0.1:49184",
+ "run_token_file": str(ROOT / "runs" / "run-token"),
+ "source_dir": str(ROOT), "controller_python": sys.executable},
+ "gpu_target": {"id": "local", "transport": "local", "ssh": None,
+ "source_dir": str(ROOT), "train_python": sys.executable,
+ "environment_file": None, "expected_gpus": expected_gpus},
+ "storage": {"mode": "shared_fs", "run_root": run_root or str(ROOT / "runs"),
+ "manager_writable": True, "gpu_writable": True, "worker_writable": True},
+ "rollout": {"provider": "embedded", "workers": [
+ {"id": f"worker-{index}", "ssh": {"host": worker}, "source_dir": str(ROOT),
+ "runner_python": sys.executable, "docker": True, "task_roots": [str(ROOT)]}
+ for index, worker in enumerate(workers)]},
+ "connectivity": {"command_timeout_s": 30, "ssh_timeout_s": 15, "http_timeout_s": 10,
+ "gpu_to_controller": True, "worker_to_gateway": True,
+ "manager_to_gpu_inference": True},
+ })
+
+
+DEFAULT_FILENAME = "deployment.yaml"
+
+
+def load_deployment(path=None, *, default_directory="."):
+ """Load an explicit, default-discovered or embedded-local deployment config.
+
+ Returns ``(config, source)`` where source describes where the config came
+ from; absent any file the embedded-local topology keeps legacy behavior.
+ """
+ if path is not None:
+ source = str(Path(path).expanduser())
+ candidates = [source]
+ else:
+ candidates = [str(Path(default_directory) / DEFAULT_FILENAME), DEFAULT_FILENAME]
+ source = candidates[0]
+ for candidate in candidates:
+ file = Path(candidate)
+ if not file.is_file():
+ continue
+ try:
+ text = file.read_text()
+ except OSError as exc:
+ raise DeploymentError(f"cannot read deployment config {candidate}: {exc}") from exc
+ try:
+ raw = yaml.safe_load(text)
+ except yaml.YAMLError as exc:
+ raise DeploymentError(f"deployment YAML {candidate}: {exc}") from exc
+ return validate(raw), str(file.resolve())
+ if path is not None:
+ raise DeploymentError(f"deployment config not found: {path}")
+ return embedded_local(), "embedded-local"
+
+
+def summary(config, source):
+ """Plan-embedded deployment digest; the full config travels with it."""
+ from harborrl.trajectories.native import digest
+ return {
+ "schema_version": DEPLOYMENT_SCHEMA_VERSION,
+ "source": source,
+ "digest": digest(config),
+ "mode": config["manager"]["mode"],
+ "manager_id": config["manager"]["id"],
+ "control_url": config["manager"]["control_url"],
+ "gateway_url": config["manager"]["gateway_url"],
+ "gpu": {"id": config["gpu_target"]["id"], "transport": config["gpu_target"]["transport"]},
+ "worker_ids": [worker["id"] for worker in config["rollout"]["workers"]],
+ "run_root": config["storage"]["run_root"],
+ "config": config,
+ }
diff --git a/harborrl/deployment/training_target.py b/harborrl/deployment/training_target.py
new file mode 100644
index 000000000..4de5d2468
--- /dev/null
+++ b/harborrl/deployment/training_target.py
@@ -0,0 +1,217 @@
+"""TrainingTarget (P1): launch, query and cancel one Slime training job.
+
+``local`` keeps the legacy in-process launcher byte-for-byte. ``ssh`` runs the
+same launcher script on the GPU machine through shared storage: the run
+directory is written by the manager, executed by the GPU target, and polled
+without holding an interactive session open.
+"""
+from __future__ import annotations
+
+import os
+import shlex
+import signal
+import subprocess
+import sys
+import time
+from pathlib import Path
+
+from harborrl.deployment import remote
+from harborrl.deployment.schema import destination
+
+LAUNCHER_NAME = "gpu-launcher.sh"
+EXIT_CODE_NAME = "exit-code"
+PID_NAME = "launcher.pid"
+STDOUT_NAME = "train-stdout.log"
+
+
+class TrainingTargetError(RuntimeError):
+ pass
+
+
+class LocalTrainingTarget:
+ """Legacy target: the Slime job runs as a child of this process."""
+
+ def __init__(self, command, *, cwd, env):
+ self.command = [str(part) for part in command]
+ self.cwd = str(cwd)
+ self.env = env
+ self.child = None
+
+ def launch(self):
+ self.child = subprocess.Popen(self.command, cwd=self.cwd, env=self.env,
+ start_new_session=True)
+ return self.child.pid
+
+ def wait(self):
+ if self.child is None:
+ raise TrainingTargetError("local target was never launched")
+
+ def forward_signal(signum, frame):
+ if self.child.poll() is None:
+ try:
+ os.killpg(os.getpgid(self.child.pid), signum)
+ except ProcessLookupError:
+ pass
+
+ previous = {sig: signal.signal(sig, forward_signal)
+ for sig in (signal.SIGINT, signal.SIGTERM)}
+ try:
+ return self.child.wait()
+ finally:
+ for sig, handler in previous.items():
+ signal.signal(sig, handler)
+
+ def query(self):
+ if self.child is None:
+ return {"state": "not-launched"}
+ return {"state": "running" if self.child.poll() is None else "exited",
+ "exit_code": self.child.returncode}
+
+ def cancel(self):
+ if self.child is None or self.child.poll() is not None:
+ return False
+ try:
+ os.killpg(os.getpgid(self.child.pid), signal.SIGINT)
+ except (ProcessLookupError, PermissionError):
+ self.child.terminate()
+ return True
+
+
+class SshTrainingTarget:
+ """Remote target over shared storage; the launcher script is the contract."""
+
+ def __init__(self, spec, *, source_dir, run_dir, environment_file=None,
+ command_timeout=60, ssh_timeout=15, poll_interval=2.0):
+ self.spec = dict(spec)
+ self.source_dir = str(source_dir)
+ self.run_dir = Path(run_dir).resolve()
+ self.environment_file = environment_file
+ self.command_timeout = command_timeout
+ self.ssh_timeout = ssh_timeout
+ self.poll_interval = poll_interval
+ self._launched = False
+
+ def write_launcher(self, command, environment, *, path_rewrites=()):
+ """Materialize the launcher; env values and paths are quoted, never interpolated."""
+ command = [str(part) for part in command]
+ for manager_path, target_path in path_rewrites:
+ command = [str(part).replace(str(manager_path), str(target_path), 1)
+ if str(part).startswith(str(manager_path)) else part for part in command]
+ lines = ["#!/usr/bin/env bash",
+ "# Generated by the HarborRL manager; executed on the GPU target.",
+ "set -uo pipefail",
+ f"cd {shlex.quote(self.source_dir)}",
+ f"export RUN_DIR={shlex.quote(str(self.run_dir))}"]
+ if self.environment_file:
+ # A controlled JSON mapping only; never an arbitrary remote shell.
+ quoted_file = shlex.quote(str(self.environment_file))
+ lines.append(
+ "eval \"$(HARBORRL_ENV_FILE=" + quoted_file + " python3 - <<'HARBORRL_ENV_JSON'\n"
+ "import json, os, shlex\n"
+ "with open(os.environ['HARBORRL_ENV_FILE']) as stream:\n"
+ " for key, value in sorted(json.load(stream).items()):\n"
+ " print(f'export {key}={shlex.quote(str(value))}')\n"
+ "HARBORRL_ENV_JSON\n"
+ ")\"")
+ for key in sorted(environment):
+ lines.append(f"export {key}={shlex.quote(str(environment[key]))}")
+ lines.append("status=0")
+ # The command is a full argv (its first word is already the
+ # interpreter); execute it as-is and capture the real exit code.
+ lines.append(" ".join(shlex.quote(part) for part in command)
+ + " || status=$?")
+ lines.append(f"printf '%s\\n' \"$status\" > {shlex.quote(str(self.run_dir / EXIT_CODE_NAME))}")
+ lines.append("exit \"$status\"")
+ launcher = self.run_dir / LAUNCHER_NAME
+ launcher.write_text("\n".join(lines) + "\n")
+ launcher.chmod(0o755)
+ return launcher
+
+ def launch(self):
+ if self._launched:
+ raise TrainingTargetError("ssh target was already launched")
+ # The background job records its own session-leader pid: setsid execs
+ # the launcher, so the recorded pid heads the cancellable process group.
+ bootstrap = (f"cd {shlex.quote(str(self.run_dir))} && "
+ f"(setsid nohup bash {LAUNCHER_NAME} > {STDOUT_NAME} 2>&1"
+ f" < /dev/null & echo $! > {PID_NAME})")
+ result = remote.run_ssh(self.spec, bootstrap, timeout=self.command_timeout,
+ connect_timeout=self.ssh_timeout)
+ if result.returncode != 0:
+ raise TrainingTargetError(
+ f"remote launch failed on {destination(self.spec)}: "
+ f"{result.stderr.strip()[-500:]}")
+ self._launched = True
+ return self.run_dir / PID_NAME
+
+ def _pid(self):
+ try:
+ return int((self.run_dir / PID_NAME).read_text().strip())
+ except (OSError, ValueError):
+ return None
+
+ def exit_code(self):
+ try:
+ return int((self.run_dir / EXIT_CODE_NAME).read_text().strip())
+ except (OSError, ValueError):
+ return None
+
+ def alive(self):
+ pid = self._pid()
+ if pid is None:
+ return False
+ result = remote.run_ssh(self.spec, f"kill -0 {pid} 2>/dev/null && echo ok",
+ timeout=self.command_timeout, connect_timeout=self.ssh_timeout)
+ return result.returncode == 0 and result.stdout.strip() == "ok"
+
+ def wait(self, *, timeout=None):
+ """Poll the shared exit-code record; never resubmit or restart."""
+ deadline = None if timeout is None else time.monotonic() + timeout
+ while True:
+ code = self.exit_code()
+ if code is not None:
+ return code
+ if deadline is not None and time.monotonic() >= deadline:
+ raise TimeoutError(f"GPU job in {self.run_dir} did not finish in time")
+ time.sleep(self.poll_interval)
+
+ def query(self):
+ code = self.exit_code()
+ return {"state": "exited" if code is not None else
+ ("running" if self.alive() else "unknown"),
+ "exit_code": code, "pid": self._pid(),
+ "run_dir": str(self.run_dir)}
+
+ def cancel(self, *, grace_seconds=30):
+ pid = self._pid()
+ if pid is None:
+ return False
+ for signame in ("INT", "TERM", "KILL"):
+ remote.run_ssh(self.spec, f"kill -{signame} -- -{pid} 2>/dev/null || "
+ f"kill -{signame} {pid} 2>/dev/null || true",
+ timeout=self.command_timeout, connect_timeout=self.ssh_timeout)
+ deadline = time.monotonic() + grace_seconds
+ while time.monotonic() < deadline:
+ if self.exit_code() is not None:
+ return True
+ time.sleep(min(2.0, self.poll_interval))
+ return self.exit_code() is not None
+
+
+def local_target(plan, *, environment):
+ """Build the legacy in-process target from a resolved plan."""
+ allowed = {"PATH", "HOME", "USER", "LANG", "LC_ALL", "TMPDIR", "LD_LIBRARY_PATH",
+ "CUDA_VISIBLE_DEVICES", "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
+ "http_proxy", "https_proxy", "no_proxy", "PYTHONPATH",
+ "SLIME_DIR", "MEGATRON_DIR", "SGLANG_IMAGE"}
+ child_env = {key: value for key, value in os.environ.items() if key in allowed}
+ child_env["PATH"] = os.pathsep.join(
+ [str(Path(sys.executable).parent), child_env.get("PATH", os.defpath)])
+ child_env["PYTHONPATH"] = os.pathsep.join(filter(None, [
+ child_env.get("SLIME_DIR", ""), child_env.get("MEGATRON_DIR", ""),
+ str(Path(__file__).resolve().parents[2]), child_env.get("PYTHONPATH", ""),
+ ]))
+ child_env.update(environment)
+ from harborrl.config import ROOT
+
+ return LocalTrainingTarget(plan["training_command"], cwd=ROOT, env=child_env)
diff --git a/harborrl/gateway/messages.py b/harborrl/gateway/messages.py
index 479e764eb..9c474817b 100644
--- a/harborrl/gateway/messages.py
+++ b/harborrl/gateway/messages.py
@@ -58,7 +58,7 @@ def convert(payload, *, model, max_output_tokens=8192, count_only=False):
raise ProtocolError("unsupported context management control")
effort = payload.get("output_config")
if effort is not None and (not isinstance(effort, dict) or set(effort) != {"effort"}
- or effort["effort"] not in ("low", "medium", "high")):
+ or effort["effort"] not in ("low", "medium", "high", "xhigh")):
raise ProtocolError("unsupported output configuration")
if not isinstance(payload.get("tools", []), list):
raise ProtocolError("tools must be a list")
@@ -138,7 +138,13 @@ def parse_qwen_output(text, *, response_id, tools):
"""Deterministic Qwen tool markers; malformed model output is never repaired."""
if "" in text or " " in text:
raise ProtocolError("thinking output is not enabled by this profile")
- names = {tool["function"]["name"] for tool in tools}
+ names = {}
+ for tool in tools:
+ name = tool["function"]["name"]
+ alias = name.casefold()
+ if alias in names:
+ raise ProtocolError("ambiguous generated tool alias")
+ names[alias] = name
blocks, offset = [], 0
matches = list(re.finditer(r"\s*(.*?)\s* ", text, re.DOTALL))
remainder = re.sub(r"\s*(.*?)\s* ", "", text, flags=re.DOTALL)
@@ -152,9 +158,17 @@ def parse_qwen_output(text, *, response_id, tools):
except ValueError as exc:
raise ProtocolError("invalid generated tool JSON") from exc
keys(call, ("name", "arguments"), "generated tool")
- if call.get("name") not in names or not isinstance(call.get("arguments"), dict):
- raise ProtocolError("unknown generated tool or invalid arguments")
- blocks.append({"type": "tool_use", "id": f"toolu_{response_id}_{index}", "name": call["name"], "input": call["arguments"]})
+ generated_name = call.get("name")
+ if generated_name == "finish" and call.get("arguments") == {}:
+ if text[match.end():]:
+ raise ProtocolError("finish marker must be the final output")
+ return blocks
+ canonical_name = names.get(generated_name.casefold()) if isinstance(generated_name, str) else None
+ if canonical_name is None:
+ raise ProtocolError(f"unknown generated tool: {generated_name!r}")
+ if not isinstance(call.get("arguments"), dict):
+ raise ProtocolError("generated tool arguments are not an object")
+ blocks.append({"type": "tool_use", "id": f"toolu_{response_id}_{index}", "name": canonical_name, "input": call["arguments"]})
offset = match.end()
if text[offset:] or not blocks:
blocks.append({"type": "text", "text": text[offset:]})
diff --git a/harborrl/gateway/server.py b/harborrl/gateway/server.py
index 282a95e26..fd011c505 100644
--- a/harborrl/gateway/server.py
+++ b/harborrl/gateway/server.py
@@ -110,7 +110,7 @@ def do_POST(self):
gateway.registry.delivery(aid, message["id"], "delivery_unknown")
status = 401 if isinstance(exc, PermissionError) else 400 if isinstance(exc, (ProtocolError, json.JSONDecodeError)) else 502
# Do not echo backend errors or request contents (credentials may appear there).
- self.reply(status, {"type": "error", "error": {"type": "authentication_error" if status == 401 else "invalid_request_error" if status == 400 else "api_error", "message": type(exc).__name__}})
+ self.reply(status, {"type": "error", "error": {"type": "authentication_error" if status == 401 else "invalid_request_error" if status == 400 else "api_error", "message": f"{type(exc).__name__}: {exc}"}})
server = ThreadingHTTPServer((host, port), Handler)
server.daemon_threads = True
diff --git a/harborrl/gateway/sglang_backend.py b/harborrl/gateway/sglang_backend.py
index f37f594d8..d2efe5e63 100644
--- a/harborrl/gateway/sglang_backend.py
+++ b/harborrl/gateway/sglang_backend.py
@@ -9,24 +9,23 @@
from harborrl.trajectories.native import digest, finite
-from .messages import ProtocolError, parse_qwen_output
+from .messages import ProtocolError
class SGLangBackend:
- def __init__(self, endpoint, tokenizer, *, tokenizer_digest, template_digest,
- timeout=120, max_context=32768, audited_raw_logprobs=False):
+ def __init__(self, endpoint, tokenizer, model_adapter, *, tokenizer_digest,
+ template_digest, timeout=120, max_context=32768,
+ audited_raw_logprobs=False):
self.endpoint = endpoint.rstrip("/")
self.tokenizer = tokenizer
+ self.model_adapter = model_adapter
self.tokenizer_digest, self.template_digest = tokenizer_digest, template_digest
self.timeout, self.max_context = timeout, max_context
self.audited_raw_logprobs = audited_raw_logprobs
self.opener = request.build_opener(request.ProxyHandler({}))
def prepare(self, converted):
- ids = self.tokenizer.apply_chat_template(converted["messages"], tools=converted["tools"] or None,
- tokenize=True, add_generation_prompt=True, enable_thinking=False)
- if not isinstance(ids, list) or not ids or any(type(i) is not int or i < 0 for i in ids):
- raise ProtocolError("tokenizer did not produce actual input IDs")
+ ids = self.model_adapter.render_chat(self.tokenizer, converted)
if len(ids) + converted["sampling"]["max_new_tokens"] > self.max_context:
raise ProtocolError("context budget exceeded; implicit truncation is forbidden")
return ids
@@ -97,7 +96,9 @@ def decode(self, output, payload, converted, identity, response_id):
finish = meta.get("finish_reason", {})
if finish.get("type") not in ("stop", "length"):
raise ValueError("unsupported serving finish reason")
- blocks = parse_qwen_output(text, response_id=response_id, tools=converted["tools"])
+ blocks = self.model_adapter.parse_output(
+ text, response_id=response_id, tools=converted["tools"]
+ )
has_tools = any(b["type"] == "tool_use" for b in blocks)
stop_sequence = finish.get("matched") if isinstance(finish.get("matched"), str) else None
if stop_sequence not in converted["sampling"]["stop"]:
diff --git a/harborrl/gateway/trace.py b/harborrl/gateway/trace.py
index a5a5d3118..5f5ca7c6a 100644
--- a/harborrl/gateway/trace.py
+++ b/harborrl/gateway/trace.py
@@ -94,6 +94,19 @@ def delivery(self, aid, response_id, status):
state["pending"].discard(matches[0]["request_id"])
self.changed.notify_all()
+ def wait_delivered(self, aid, timeout):
+ deadline = time.monotonic() + timeout
+ with self.changed:
+ state = self.attempts[aid]
+ while state["pending"]:
+ remaining = deadline - time.monotonic()
+ if remaining <= 0:
+ raise TimeoutError("policy delivery acknowledgement still pending")
+ self.changed.wait(remaining)
+ if any(turn.get("delivery") not in ("sent", "consumed_confirmed")
+ for turn in state["turns"]):
+ raise ValueError("policy response delivery was not confirmed")
+
def consume(self, aid, response_id, content, evidence_ref):
"""Called only by trusted native-session audit, never by an HTTP policy client."""
with self.lock:
@@ -109,6 +122,13 @@ def consume(self, aid, response_id, content, evidence_ref):
publish(state["root"] / "consumption" / f"{response_id}.json", {"response_id": response_id, "content_digest": digest(content), "evidence_ref": evidence_ref})
turn.update(delivery="consumed_confirmed", consumption_ref=evidence_ref)
+ def unconsumed_responses(self, aid):
+ with self.lock:
+ state = self.attempts[aid]
+ return [{"response_id": turn["response_id"], "content": deepcopy(turn["response"]["content"]),
+ "finish_reason": turn.get("finish_reason"), "is_final": index == len(state["turns"]) - 1}
+ for index, turn in enumerate(state["turns"]) if turn.get("delivery") != "consumed_confirmed"]
+
def close(self, aid):
with self.lock:
state = self.attempts[aid]
diff --git a/harborrl/harnesses/__init__.py b/harborrl/harnesses/__init__.py
new file mode 100644
index 000000000..53da87865
--- /dev/null
+++ b/harborrl/harnesses/__init__.py
@@ -0,0 +1 @@
+"""Agent harness adapters and their protocol boundaries."""
diff --git a/harborrl/harnesses/claude_code.py b/harborrl/harnesses/claude_code.py
new file mode 100644
index 000000000..fb5c27519
--- /dev/null
+++ b/harborrl/harnesses/claude_code.py
@@ -0,0 +1,53 @@
+"""Claude Code harness adapter with explicit credentials and session audit."""
+from __future__ import annotations
+
+import hashlib
+import json
+from pathlib import Path
+
+from harborrl.rollout.harbor_job.audit import audit_session
+from harborrl.rollout.harbor_job.bindings import (
+ claude_config,
+ sanitized_runner_env,
+)
+
+
+class ClaudeCodeHarnessAdapter:
+ harness_id = "claude_code"
+
+ def validate_profile(self, profile, gateway_url):
+ return claude_config(profile, gateway_url, "validation-only")
+
+ def agent_config(self, profile, gateway_url, credential, max_output_tokens=8192):
+ return claude_config(profile, gateway_url, credential, max_output_tokens)
+
+ def runner_environment(self, environment):
+ return sanitized_runner_env(environment)
+
+ def audit_session(self, path, registry, attempt_id):
+ return audit_session(path, registry, attempt_id)
+
+ def audit_empty_final(self, path, registry, attempt_id, audit):
+ """Claude 2.1.141 omits empty final messages from persisted sessions."""
+ missing = registry.unconsumed_responses(attempt_id)
+ if not missing:
+ return
+ if len(missing) != 1 or missing[0] != {
+ "response_id": missing[0]["response_id"], "content": [],
+ "finish_reason": "end_turn", "is_final": True,
+ }:
+ raise ValueError("only one empty final end_turn may use terminal evidence")
+ path = Path(path)
+ raw = path.read_bytes()
+ events = [json.loads(line) for line in raw.splitlines() if line.strip()]
+ results = [event for event in events if event.get("type") == "result"]
+ if len(results) != 1 or events[-1] != results[0]:
+ raise ValueError("expected one final Claude result")
+ result = results[0]
+ expected = {"subtype": "success", "is_error": False, "result": "",
+ "stop_reason": "end_turn", "session_id": audit["session_id"],
+ "terminal_reason": "completed"}
+ if any(result.get(key) != value for key, value in expected.items()) or result.get("is_error") is not False:
+ raise ValueError("empty final response lacks successful matching session result")
+ evidence = f"{path}#sha256={hashlib.sha256(raw).hexdigest()}"
+ registry.consume(attempt_id, missing[0]["response_id"], [], evidence)
diff --git a/harborrl/harnesses/offline_claude.py b/harborrl/harnesses/offline_claude.py
new file mode 100644
index 000000000..cd44d37cd
--- /dev/null
+++ b/harborrl/harnesses/offline_claude.py
@@ -0,0 +1,61 @@
+"""Pinned native Claude executable deployment, without package-manager fallback."""
+import asyncio
+import hashlib
+import json
+import shlex
+from pathlib import Path
+
+from harbor.agents.installed.claude_code import ClaudeCode
+
+from harborrl.trajectories.native import publish
+
+
+def binary_checksum(binary):
+ with binary.open('rb') as source:
+ return hashlib.file_digest(source, 'sha256').hexdigest()
+
+
+class OfflineClaudeCode(ClaudeCode):
+ def __init__(self, *args, installation, **kwargs):
+ self.installation = installation
+ super().__init__(*args, **kwargs)
+
+ async def install(self, environment):
+ lock = self.installation
+ binary = Path(lock['binary'])
+ checksum = await asyncio.to_thread(binary_checksum, binary)
+ if checksum != lock['sha256']:
+ raise ValueError('offline Claude binary checksum mismatch')
+ # Harbor invokes Claude with this HOME-local prefix on every command.
+ probe = ('import json, os, platform; print(json.dumps(dict('
+ 'system=platform.system(), machine=platform.machine(), '
+ 'libc=os.confstr("CS_GNU_LIBC_VERSION"), home=os.environ.get("HOME", ""))))')
+ result = await environment.exec('python3 -c ' + shlex.quote(probe))
+ try:
+ system = json.loads(result.stdout or '')
+ except ValueError:
+ system = {}
+ if (result.return_code or system.get('system') != 'Linux' or system.get('machine') != 'x86_64'
+ or not str(system.get('libc', '')).startswith('glibc ')):
+ raise ValueError('offline binary requires Linux x86_64 glibc and a writable HOME')
+ home = system.get('home', '')
+ if not home.startswith('/') or home == '/':
+ raise ValueError('invalid agent HOME')
+ target = home + '/.local/bin/claude'
+ staging = target + '.harborrl-upload'
+ result = await environment.exec('mkdir -p ' + shlex.quote(home + '/.local/bin'))
+ if result.return_code:
+ raise ValueError('agent tool directory is not writable')
+ await environment.upload_file(binary, staging)
+ command = (f'printf "%s %s\\n" {shlex.quote(lock["sha256"])} {shlex.quote(staging)} | sha256sum -c -'
+ f' && chmod 755 {shlex.quote(staging)} && mv {shlex.quote(staging)} {shlex.quote(target)}')
+ result = await environment.exec(command)
+ if result.return_code:
+ raise ValueError('offline Claude installation checksum/permissions failed')
+ result = await environment.exec(f'{shlex.quote(target)} --version')
+ version = (result.stdout or '').strip().split()
+ if result.return_code or not version or version[0] != lock['version']:
+ raise ValueError('offline Claude installation/version probe failed')
+ publish(self.logs_dir / 'offline-installation.json', {
+ 'version': lock['version'], 'sha256': lock['sha256'], 'target': target,
+ 'platform': system, 'mode': lock['mode']})
diff --git a/harborrl/math_registry.py b/harborrl/math_registry.py
new file mode 100644
index 000000000..61f0a2b37
--- /dev/null
+++ b/harborrl/math_registry.py
@@ -0,0 +1,316 @@
+"""Explicit MATH component contracts and startup compatibility checks."""
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Protocol
+
+from harborrl.algorithms.grpo import GRPOAlgorithmAdapter
+from harborrl.backends.slime_v032.versions import BACKEND_CONTRACT
+from harborrl.harnesses.claude_code import ClaudeCodeHarnessAdapter
+from harborrl.models.qwen import QwenModelAdapter
+from harborrl.tasks.jsonl_provider import JsonlTaskProvider
+from harborrl.tasks.provider import NativeTaskProvider
+
+
+class ComponentRegistry:
+ def __init__(self, dimension):
+ self.dimension = dimension
+ self._components = {}
+
+ def register(self, spec):
+ if spec.id in self._components:
+ raise ValueError(f"duplicate {self.dimension} registration: {spec.id}")
+ self._components[spec.id] = spec
+ return spec
+
+ def get(self, component_id):
+ try:
+ return self._components[component_id]
+ except KeyError as exc:
+ raise ValueError(
+ f"unsupported {self.dimension}: {component_id}; "
+ f"registered: {', '.join(sorted(self._components))}"
+ ) from exc
+
+ def items(self):
+ return tuple(self._components[spec_id] for spec_id in sorted(self._components))
+
+
+@dataclass(frozen=True)
+class ModelSpec:
+ id: str
+ display_name: str
+ preset_prefix: str
+ serving_backend: str
+ tool_protocol: str
+ gateway_protocols: frozenset[str]
+ training_backend_contracts: frozenset[str]
+ adapter: QwenModelAdapter
+ version: str
+ evidence: tuple[str, ...]
+ limitations: tuple[str, ...]
+
+
+@dataclass(frozen=True)
+class AlgorithmSpec:
+ id: str
+ display_name: str
+ training_backend_contracts: frozenset[str]
+ minimum_group_size: int
+ requires_audited_raw_logprobs: bool
+ reduction: str
+ adapter: GRPOAlgorithmAdapter
+ backend_hooks: dict[str, str]
+ version: str
+ evidence: tuple[str, ...]
+ limitations: tuple[str, ...]
+
+
+@dataclass(frozen=True)
+class TaskProviderSpec:
+ id: str
+ display_name: str
+ execution_backends: frozenset[str]
+ task_format: str
+ adapter: TaskProviderAdapter
+ version: str
+ evidence: tuple[str, ...]
+ limitations: tuple[str, ...]
+
+
+@dataclass(frozen=True)
+class HarnessSpec:
+ id: str
+ display_name: str
+ request_protocol: str
+ tool_protocol: str
+ harbor_versions: frozenset[str]
+ adapter: ClaudeCodeHarnessAdapter
+ version: str
+ evidence: tuple[str, ...]
+ limitations: tuple[str, ...]
+
+
+class ModelAdapter(Protocol):
+ def render_chat(self, tokenizer, converted): ...
+
+ def parse_output(self, text, *, response_id, tools): ...
+
+
+class AlgorithmAdapter(Protocol):
+ def compute_advantages_and_returns(self, args, rollout_data): ...
+
+
+class TaskAdapter(Protocol):
+ def discover(self, tasks_dir): ...
+
+ def inspect(self, path, *, expected_digest=None): ...
+
+ def catalog(self, config): ...
+
+
+class TaskProviderAdapter(Protocol):
+ def discover(self, tasks_dir): ...
+
+ def inspect(self, path, *, expected_digest=None): ...
+
+ def catalog(self, config): ...
+
+ def prompt_rows(self, entries): ...
+
+
+class HarnessAdapter(Protocol):
+ def validate_profile(self, profile, gateway_url): ...
+
+ def agent_config(self, profile, gateway_url, credential, max_output_tokens=8192): ...
+
+QWEN_MODEL = ModelSpec(
+ id="qwen",
+ display_name="Qwen",
+ preset_prefix="qwen",
+ serving_backend="sglang",
+ tool_protocol="qwen-tool-call-v1",
+ gateway_protocols=frozenset({"anthropic-messages-v1"}),
+ training_backend_contracts=frozenset({BACKEND_CONTRACT}),
+ adapter=QwenModelAdapter(),
+ version="qwen3-profile-v1",
+ evidence=("examples/native/train_qwen_native.yaml",),
+ limitations=("preset must start with qwen", "tokenizer and template digests are checked live"),
+)
+GRPO_ALGORITHM = AlgorithmSpec(
+ id="grpo",
+ display_name="Slime GRPO",
+ training_backend_contracts=frozenset({BACKEND_CONTRACT}),
+ minimum_group_size=2,
+ requires_audited_raw_logprobs=True,
+ reduction="global-sum",
+ adapter=GRPOAlgorithmAdapter(),
+ backend_hooks={
+ "rollout": "harborrl.backends.slime_v032.rollout.generate_rollout",
+ "converter": "harborrl.backends.slime_v032.converter.convert_samples_to_train_data",
+ "advantage": "harborrl.backends.slime_v032.advantage.compute_advantages_and_returns",
+ "loss": "harborrl.backends.slime_v032.loss.loss_function",
+ "postprocess": "harborrl.backends.slime_v032.postprocess.rollout_data_postprocess",
+ },
+ version="native-centered-grpo-v1",
+ evidence=("harborrl/backends/slime_v032/loss.py",),
+ limitations=("requires complete reward-varied groups", "only Slime v0.3.2 hooks are accepted"),
+)
+HARBOR_JSONL_TASK_PROVIDER = TaskProviderSpec(
+ id="harbor_jsonl",
+ display_name="Harbor JSONL",
+ execution_backends=frozenset({"harbor_job"}),
+ task_format="native_single_step_jsonl_v1",
+ adapter=JsonlTaskProvider(),
+ version="jsonl-catalog-lock-v1",
+ evidence=("examples/native/catalog.jsonl",),
+ limitations=(
+ "one task lock must be encoded per JSONL line",
+ "tasks must be fetched before cataloging",
+ "task selectors use catalog IDs",
+ ),
+)
+NATIVE_TASK_PROVIDER = TaskProviderSpec(
+ id="native_harbor",
+ display_name="Harbor",
+ execution_backends=frozenset({"harbor_job"}),
+ task_format="native_single_step_text_v1",
+ adapter=NativeTaskProvider(),
+ version="catalog-lock-v1",
+ evidence=("examples/native/catalog.json",),
+ limitations=("tasks must be fetched before cataloging", "task selectors use catalog IDs"),
+)
+CLAUDE_CODE_HARNESS = HarnessSpec(
+ id="claude_code",
+ display_name="Claude Code",
+ request_protocol="anthropic-messages-v1",
+ tool_protocol="qwen-tool-call-v1",
+ harbor_versions=frozenset({"0.23.0"}),
+ adapter=ClaudeCodeHarnessAdapter(),
+ version="claude-code-session-audit-v1",
+ evidence=("harborrl/rollout/harbor_job/audit.py",),
+ limitations=("exact CLI version is required", "ambient credentials are rejected"),
+)
+class MATHRegistry:
+ def __init__(self):
+ self.models = ComponentRegistry("model")
+ self.algorithms = ComponentRegistry("algorithm")
+ self.tasks = ComponentRegistry("task provider")
+ self.harnesses = ComponentRegistry("harness")
+ self.models.register(QWEN_MODEL)
+ self.algorithms.register(GRPO_ALGORITHM)
+ self.tasks.register(NATIVE_TASK_PROVIDER)
+ self.tasks.register(HARBOR_JSONL_TASK_PROVIDER)
+ self.harnesses.register(CLAUDE_CODE_HARNESS)
+
+ def model_for_preset(self, preset):
+ for spec in self.models.items():
+ if str(preset).startswith(spec.preset_prefix):
+ return spec
+ raise ValueError(
+ f"unsupported model preset: {preset}; "
+ f"registered: {', '.join(spec.id for spec in self.models.items())}"
+ )
+
+ def algorithm_for_contract(self, contract):
+ for spec in self.algorithms.items():
+ if contract in spec.training_backend_contracts:
+ return spec
+ raise ValueError(f"no registered algorithm supports backend contract: {contract}")
+
+
+MATH_REGISTRY = MATHRegistry()
+
+
+@dataclass(frozen=True)
+class MATHSelection:
+ model: ModelSpec
+ algorithm: AlgorithmSpec
+ task: TaskProviderSpec
+ harness: HarnessSpec
+
+ def asdict(self):
+ return {
+ "model": _describe(self.model),
+ "algorithm": _describe(self.algorithm),
+ "task": _describe(self.task),
+ "harness": _describe(self.harness),
+ }
+
+
+def _describe(spec):
+ return {
+ "id": spec.id,
+ "display_name": spec.display_name,
+ "version": spec.version,
+ "evidence": list(spec.evidence),
+ "limitations": list(spec.limitations),
+ }
+
+
+def select_math(config):
+ return MATHSelection(
+ model=MATH_REGISTRY.model_for_preset(config["model"]["args_file"]),
+ algorithm=MATH_REGISTRY.algorithm_for_contract(
+ config["training"]["backend_contract"]
+ ),
+ task=MATH_REGISTRY.tasks.get(
+ "harbor_jsonl"
+ if str(config.get("tasks", {}).get("catalog", "")).endswith(".jsonl")
+ else "native_harbor"
+ ),
+ harness=MATH_REGISTRY.harnesses.get(config["harness"]["name"]),
+ )
+
+
+def validate_combination(selection, config):
+ contract = config["training"]["backend_contract"]
+ if contract not in selection.model.training_backend_contracts:
+ raise ValueError(
+ f"model {selection.model.id} does not support training backend {contract}"
+ )
+ if contract not in selection.algorithm.training_backend_contracts:
+ raise ValueError(
+ f"algorithm {selection.algorithm.id} does not support training backend {contract}"
+ )
+ if config["execution"]["backend"] not in selection.task.execution_backends:
+ raise ValueError(
+ f"task provider {selection.task.id} does not support execution backend "
+ f"{config['execution']['backend']}"
+ )
+ if config["harbor"]["version"] not in selection.harness.harbor_versions:
+ raise ValueError(
+ f"harness {selection.harness.id} does not support Harbor "
+ f"{config['harbor']['version']}"
+ )
+ if selection.model.tool_protocol != selection.harness.tool_protocol:
+ raise ValueError("model and harness tool protocols do not match")
+ if selection.harness.request_protocol not in selection.model.gateway_protocols:
+ raise ValueError(
+ f"model gateway does not support harness protocol {selection.harness.request_protocol}"
+ )
+ if config["sampling"]["group_size"] < selection.algorithm.minimum_group_size:
+ raise ValueError(
+ f"algorithm {selection.algorithm.id} requires group size >= "
+ f"{selection.algorithm.minimum_group_size}"
+ )
+ if (
+ selection.algorithm.requires_audited_raw_logprobs
+ and not config["gateway"]["audited_raw_logprobs"]
+ ):
+ raise ValueError(
+ f"algorithm {selection.algorithm.id} requires audited raw logprobs"
+ )
+ return selection
+
+
+def list_components():
+ return {
+ dimension: [_describe(spec) for spec in registry.items()]
+ for dimension, registry in (
+ ("models", MATH_REGISTRY.models),
+ ("algorithms", MATH_REGISTRY.algorithms),
+ ("tasks", MATH_REGISTRY.tasks),
+ ("harnesses", MATH_REGISTRY.harnesses),
+ )
+ }
diff --git a/harborrl/models/__init__.py b/harborrl/models/__init__.py
new file mode 100644
index 000000000..89314d47e
--- /dev/null
+++ b/harborrl/models/__init__.py
@@ -0,0 +1 @@
+"""Model family adapters used by serving and training boundaries."""
diff --git a/harborrl/models/qwen.py b/harborrl/models/qwen.py
new file mode 100644
index 000000000..cbd096dad
--- /dev/null
+++ b/harborrl/models/qwen.py
@@ -0,0 +1,39 @@
+"""Qwen tokenizer/template and generated tool-call protocol."""
+from __future__ import annotations
+
+from harborrl.gateway.messages import ProtocolError, parse_qwen_output
+
+
+def normalize_chat_template_ids(rendered):
+ """Accept both legacy lists and modern ``BatchEncoding`` results."""
+ if hasattr(rendered, "get"):
+ rendered = rendered.get("input_ids")
+ if isinstance(rendered, (list, tuple)):
+ if rendered and isinstance(rendered[0], (list, tuple)):
+ rendered = rendered[0]
+ return list(rendered)
+ return rendered
+
+
+class QwenModelAdapter:
+ model_id = "qwen"
+
+ def render_chat(self, tokenizer, converted):
+ rendered_ids = tokenizer.apply_chat_template(
+ converted["messages"],
+ tools=converted["tools"] or None,
+ tokenize=True,
+ add_generation_prompt=True,
+ enable_thinking=False,
+ )
+ token_ids = normalize_chat_template_ids(rendered_ids)
+ if (
+ not isinstance(token_ids, list)
+ or not token_ids
+ or any(type(token_id) is not int or token_id < 0 for token_id in token_ids)
+ ):
+ raise ProtocolError("tokenizer did not produce actual input IDs")
+ return token_ids
+
+ def parse_output(self, text, *, response_id, tools):
+ return parse_qwen_output(text, response_id=response_id, tools=tools)
diff --git a/harborrl/options/__init__.py b/harborrl/options/__init__.py
new file mode 100644
index 000000000..a53f7a1cc
--- /dev/null
+++ b/harborrl/options/__init__.py
@@ -0,0 +1,11 @@
+"""Offline configuration discovery and generation; no optional runtime imports."""
+
+
+def build_catalog():
+ from .catalog import build_catalog as build
+ return build()
+
+
+def build_config_bundle(answers, *, output_directory):
+ from .wizard import build_config_bundle as build
+ return build(answers, output_directory=output_directory)
diff --git a/harborrl/options/catalog.py b/harborrl/options/catalog.py
new file mode 100644
index 000000000..76ede0794
--- /dev/null
+++ b/harborrl/options/catalog.py
@@ -0,0 +1,147 @@
+"""JSON-safe option metadata. Existing validators remain authoritative."""
+from copy import deepcopy
+from importlib.resources import files
+
+import yaml
+
+from harborrl.config import ROOT
+from harborrl.config.native import FIELDS, OPTIONAL
+from harborrl.deployment import schema
+from harborrl.math_registry import MATH_REGISTRY
+
+from .presets import PRESETS
+
+TRAINING_FIELDS = {
+ "consistency.mode": ("string", "off, audit or enforce"),
+ "consistency.max_abs_delta": ("number_or_null", "Explicit deployment maximum token log-probability delta"),
+ "consistency.max_mean_abs_delta": ("number_or_null", "Explicit deployment mean token delta"),
+ "consistency.bad_token_delta": ("number_or_null", "Threshold identifying a bad token"),
+ "consistency.max_bad_fraction": ("number", "Allowed bad-token fraction between zero and one"),
+ "consistency.worst_tokens": ("integer", "Maximum diagnostic tokens, zero through 32"),
+ "execution.backend": ("string", "Task execution backend; currently harbor_job"),
+ "tasks.catalog": ("path", "Existing JSON/JSONL Harbor task lock; resolved on the controller"),
+ "harness.name": ("string", "Registered agent harness; currently claude_code"),
+ "harness.profile": ("path", "Pinned harness profile JSON on the controller"),
+ "harbor.python": ("path", "Runner interpreter on the local runner or SSH worker"),
+ "harbor.version": ("string", "Harbor version supported by the selected harness"),
+ "harbor.workers": ("array", "Ordered SSH destinations; empty with a local runner"),
+ "harbor.runner_transport": ("string", "local or ssh; local requires no workers"),
+ "harbor.environment": ("object", "Docker runtime, or Sandbox runtime with a config file reference"),
+ "gateway.host": ("string", "Gateway listener address, distinct from its advertised origin"),
+ "gateway.port": ("integer", "Gateway listener port, 1 through 65535"),
+ "gateway.advertised_url": ("string", "Plain HTTP(S) origin reachable by runners; port must match"),
+ "gateway.tokenizer_digest": ("string", "SHA-256 of the actual tokenizer fingerprint; never a placeholder"),
+ "gateway.template_digest": ("string", "SHA-256 of the actual chat-template fingerprint"),
+ "gateway.audited_raw_logprobs": ("boolean", "Raw serving probability audit, required by GRPO"),
+ "model.checkpoint": ("path", "Student model checkpoint; must be accessible on the GPU target"),
+ "model.reference": ("path", "Reference checkpoint accessible on the GPU target"),
+ "model.args_file": ("string", "Supported Slime preset name; currently the qwen prefix"),
+ "training.num_rollout": ("integer", "Positive number of rollout batches"),
+ "training.learning_rate": ("number", "Positive finite learning rate"),
+ "training.save_interval": ("integer", "Positive checkpoint interval in rollout batches"),
+ "training.backend_contract": ("string", "Pinned backend/algorithm contract"),
+ "sampling.group_size": ("integer", "Samples per prompt, at least two for GRPO"),
+ "sampling.groups_per_batch": ("integer", "Positive number of complete groups per batch"),
+ "sampling.max_attempts": ("integer", "Bounded invalid-attempt retries; not low-reward retries"),
+ "sampling.max_tokens": ("integer", "Positive response limit below max_context"),
+ "sampling.max_context": ("integer", "Total context token limit"),
+ "deployment.layout": ("string", "split or colocate; controls actor/serving GPU allocation"),
+ "deployment.num_gpus": ("integer", "Total positive GPU budget, not a memory-fit guarantee"),
+ "deployment.actor_gpus": ("integer", "Actor GPU count; must be divisible by actor TP"),
+ "deployment.rollout_gpus": ("integer", "Serving GPU count; must be divisible by engine TP"),
+ "deployment.actor_tensor_parallel_size": ("integer", "Positive actor tensor-parallel size"),
+ "deployment.rollout_gpus_per_engine": ("integer", "Positive serving tensor-parallel size"),
+ "output.root": ("path", "Run output root; service mode must match shared storage.run_root"),
+}
+
+DEPLOYMENT_GROUPS = {
+ "manager": schema.MANAGER_FIELDS, "gpu_target": schema.GPU_FIELDS,
+ "storage": schema.STORAGE_FIELDS, "rollout": schema.ROLLOUT_FIELDS,
+ "rollout.workers[]": schema.WORKER_FIELDS, "gpu_target.ssh": schema.SSH_FIELDS,
+ "rollout.workers[].ssh": schema.SSH_FIELDS, "connectivity": schema.CONNECTIVITY_FIELDS,
+}
+DEPLOYMENT_FIELDS = {
+ "id": ("string", "Short role identifier"), "mode": ("string", "Manager mode or shared storage mode"),
+ "bind": ("string", "Manager listener address"),
+ "control_port": ("integer", "Control listener port; different from gateway_port"),
+ "control_url": ("string", "Control origin reachable from the GPU target"),
+ "gateway_port": ("integer", "Gateway listener port"),
+ "gateway_url": ("string", "Gateway origin reachable from runners or the configured relay"),
+ "run_token_file": ("path", "Manager run-token file reference; never the secret value"),
+ "source_dir": ("path", "Absolute source checkout path on this role's machine"),
+ "controller_python": ("path", "Absolute interpreter path on the controller"),
+ "transport": ("string", "GPU launch transport: local or ssh"),
+ "ssh": ("object", "SSH specification; GPU local transport requires null"),
+ "train_python": ("path", "Absolute interpreter path on the GPU target"),
+ "environment_file": ("path_or_null", "Controlled JSON environment file on the GPU target"),
+ "expected_gpus": ("integer", "Expected target GPU inventory, at least the requested budget"),
+ "run_root": ("path", "Absolute shared run root visible to all participating roles"),
+ "manager_writable": ("boolean", "Declared shared-root access; verified by doctor"),
+ "gpu_writable": ("boolean", "Declared GPU shared-root access; verified by doctor"),
+ "worker_writable": ("boolean", "Declared worker shared-root access; verified by doctor"),
+ "provider": ("string", "Rollout placement/provider; not a dataset or algorithm"),
+ "workers": ("array", "Ordered worker role specifications; empty for local Sandbox runners"),
+ "runner_python": ("path", "Absolute interpreter path on the worker"),
+ "docker": ("boolean", "Worker Docker requirement"),
+ "task_roots": ("array", "Absolute task roots visible on this worker"),
+ "host": ("string", "SSH alias/destination, not an automatically verified host"),
+ "user": ("string", "Optional SSH username"), "port": ("integer", "Optional SSH port"),
+ "identity": ("path", "Optional identity-file reference; no private-key contents"),
+ "command_timeout_s": ("integer", "Positive remote command timeout"),
+ "ssh_timeout_s": ("integer", "Positive SSH connection timeout"),
+ "http_timeout_s": ("integer", "Positive HTTP check timeout"),
+ "gpu_to_controller": ("boolean", "Declared GPU-to-controller connectivity requirement"),
+ "worker_to_gateway": ("boolean", "Declared runner-to-gateway connectivity requirement"),
+ "manager_to_gpu_inference": ("boolean", "Declared controller-to-serving connectivity requirement"),
+}
+
+
+def _describe(spec, attributes):
+ record = {}
+ for key in ("id", "display_name", "version", "evidence", "limitations", *attributes):
+ value = getattr(spec, key)
+ record[key] = sorted(value) if isinstance(value, frozenset) else list(value) if isinstance(value, tuple) else value
+ return record
+
+
+def dataset_metadata():
+ return yaml.safe_load(files("harborrl.options").joinpath("resources/datasets.yaml").read_text(encoding="utf-8"))
+
+
+def build_catalog():
+ training = {}
+ for section, fields in sorted(FIELDS.items()):
+ for name in sorted(fields):
+ key = f"{section}.{name}"
+ kind, description = TRAINING_FIELDS[key]
+ training[key] = {"type": kind, "description": description,
+ "required": name not in OPTIONAL.get(section, set())}
+ deployment = {}
+ for section, fields in sorted(DEPLOYMENT_GROUPS.items()):
+ for name in sorted(fields):
+ kind, description = DEPLOYMENT_FIELDS[name]
+ deployment[f"{section}.{name}"] = {"type": kind, "description": description}
+ for key, values in {"manager.mode": ("embedded", "service"), "storage.mode": schema.STORAGE_MODES,
+ "rollout.provider": schema.ROLLOUT_PROVIDERS, "gpu_target.transport": schema.TRANSPORTS}.items():
+ deployment[key]["values"] = list(values)
+ datasets = deepcopy(dataset_metadata()["datasets"])
+ for item in datasets:
+ path = ROOT / item["task_root"]
+ item["available"] = item["status"] != "planned" and item["source"] == "bundled" and path.is_dir()
+ item["availability_note"] = "Provide an existing inspected task catalog; no download is performed"
+ registry = MATH_REGISTRY
+ return {
+ "catalog_version": 1, "schema_versions": {"training": 2, "deployment": 1},
+ "models": [_describe(s, ("preset_prefix", "serving_backend", "tool_protocol", "gateway_protocols", "training_backend_contracts")) for s in registry.models.items()],
+ "algorithms": [_describe(s, ("training_backend_contracts", "minimum_group_size", "requires_audited_raw_logprobs", "reduction")) for s in registry.algorithms.items()],
+ "task_providers": [_describe(s, ("execution_backends", "task_format")) for s in registry.tasks.items()],
+ "harnesses": [_describe(s, ("request_protocol", "tool_protocol", "harbor_versions")) for s in registry.harnesses.items()],
+ "datasets": datasets, "deployment_presets": deepcopy(list(PRESETS)),
+ "fields": {"training": training, "deployment": deployment},
+ "compatibility": {"scope": "component_metadata", "status": "unknown_until_configuration_validated",
+ "constraints": ["Model and harness tool/request protocols must match",
+ "Algorithm and model must support the selected backend contract",
+ "Task provider must support the execution backend",
+ "Harbor version must be supported by the harness"],
+ "validator": "harborrl.math_registry.validate_combination"},
+ }
diff --git a/harborrl/options/cli.py b/harborrl/options/cli.py
new file mode 100644
index 000000000..7438628dc
--- /dev/null
+++ b/harborrl/options/cli.py
@@ -0,0 +1,184 @@
+"""Terminal interface for the offline catalog, validation and bundle builder."""
+import argparse
+import json
+import shlex
+import sys
+from pathlib import Path
+
+import yaml
+
+from .catalog import build_catalog
+from .presets import PRESETS, worker_defaults
+from .validation import read_mapping, validate_paths
+from .wizard import build_config_bundle, publish_bundle
+
+DIMENSIONS = {"models": "models", "algorithms": "algorithms", "tasks": "task_providers",
+ "harnesses": "harnesses", "datasets": "datasets", "deployments": "deployment_presets",
+ "fields": "fields", "compat": "compatibility"}
+
+
+def ask(label, default=None, convert=str):
+ while True:
+ value = input(f"{label}" + (f" [{default}]" if default is not None else "") + ": ").strip()
+ if not value and default is not None:
+ return default
+ try:
+ if not value:
+ raise ValueError("A value is required")
+ return convert(value)
+ except ValueError as exc:
+ print(str(exc), file=sys.stderr)
+
+
+def choose(label, choices):
+ for index, value in enumerate(choices, 1):
+ print(f" {index}. {value}")
+ while True:
+ index = ask(label, 1, int)
+ if 1 <= index <= len(choices):
+ return choices[index - 1]
+ print("Choose a listed number", file=sys.stderr)
+
+
+def interactive_answers():
+ preset = choose("Deployment", [p["id"] for p in PRESETS])
+ t, d = {}, {}
+ service = preset != "embedded-local"
+ print("Components: Qwen / GRPO / Harbor tasks / Claude Code. Advanced fields use an answers file.")
+ if service:
+ d["manager"] = {
+ "source_dir": ask("Controller source directory"),
+ "controller_python": ask("Controller Python", sys.executable),
+ "control_url": ask("Controller origin (include port 49200)"),
+ "gateway_url": ask("Gateway origin (include port 49184)"),
+ }
+ d["storage"] = {"run_root": ask("Shared run root (absolute)")}
+ d["gpu_target"] = {"ssh": {"host": ask("GPU SSH alias/destination")},
+ "source_dir": ask("GPU source directory"), "train_python": ask("GPU training Python")}
+ else:
+ d["gpu_target"] = {"train_python": ask("Training Python", sys.executable)}
+ if preset == "service-ssh":
+ workers = []
+ count = ask("Number of SSH runners", 1, int)
+ if count < 1:
+ raise ValueError("At least one SSH runner is required")
+ for index in range(count):
+ worker = worker_defaults()
+ worker.update(id=f"worker-{index}", ssh={"host": ask(f"Runner {index} SSH destination")},
+ source_dir=ask("Runner source directory"), runner_python=ask("Runner Python"),
+ task_roots=[ask("Runner task root")])
+ workers.append(worker)
+ d["rollout"] = {"workers": workers}
+ else:
+ t["harbor"] = {"python": ask("Local Harbor runner Python", sys.executable)}
+ t["tasks"] = {"catalog": ask("Existing task catalog JSON/JSONL")}
+ t["harness"] = {"profile": ask("Existing harness profile JSON")}
+ t["model"] = {"checkpoint": ask("GPU checkpoint directory"), "reference": ask("GPU reference directory"),
+ "args_file": ask("Qwen model args preset", "qwen3-8B")}
+ t["gateway"] = {"tokenizer_digest": ask("Actual tokenizer SHA-256"),
+ "template_digest": ask("Actual template SHA-256")}
+ if not service:
+ t["gateway"].update(host=ask("Gateway bind address", "127.0.0.1"),
+ advertised_url=ask("Gateway origin reachable by tasks", "http://127.0.0.1:49184"))
+ layout = choose("GPU layout", ["colocate", "split"])
+ t["deployment"] = {"layout": layout, "num_gpus": ask("Total GPU budget", 1, int)}
+ if layout == "split":
+ t["deployment"].update(actor_gpus=ask("Actor GPUs", 1, int), rollout_gpus=ask("Serving GPUs", 1, int))
+ t["training"] = {"num_rollout": ask("Rollout batches", 2, int)}
+ t["sampling"] = {"group_size": ask("Samples per group", 2, int),
+ "groups_per_batch": ask("Groups per batch", 1, int)}
+ answers = {"answers_version": 1, "preset": preset, "training": t, "deployment": d}
+ if preset == "service-sandbox-local-runner":
+ answers["sandbox_config"] = ask("Existing Sandbox configuration")
+ return answers
+
+
+def render_validation(result):
+ print(f"{result['status']} (scope: {result['scope']})")
+ for row in result["checks"]:
+ print(f" {row['status']}: {row['path']}: {row['message']}")
+
+
+def render_catalog(result):
+ print("HarborRL options (catalog 1; training schema 2; deployment schema 1)")
+ for name, value in result.items():
+ if name in {"catalog_version", "schema_versions"}:
+ continue
+ if name == "fields" and len(result) > 3:
+ print("fields: use 'harborrl options fields' for field descriptions")
+ elif isinstance(value, list):
+ print(f"{name}:")
+ for row in value:
+ description = row.get("description", row.get("display_name", ""))
+ if "available" in row:
+ description += f" (status={row['status']}, locally_available={row['available']})"
+ print(f" {row.get('id', row.get('name'))}: {description}")
+ else:
+ print(yaml.safe_dump({name: value}, sort_keys=False).rstrip())
+
+
+def main(argv):
+ parser = argparse.ArgumentParser(prog="harborrl")
+ commands = parser.add_subparsers(dest="command", required=True)
+ options = commands.add_parser("options", help="List supported configuration options")
+ options.add_argument("dimension", nargs="?", choices=DIMENSIONS)
+ options.add_argument("--json", action="store_true")
+ validate = commands.add_parser("validate", help="Validate offline; no runtime is started")
+ validate.add_argument("config")
+ validate.add_argument("--deployment")
+ validate.add_argument("--check-files", action="store_true")
+ validate.add_argument("--json", action="store_true")
+ init = commands.add_parser("init", help="Create a configuration bundle in a new directory")
+ init.add_argument("--out-dir", required=True)
+ init.add_argument("--answers")
+ init.add_argument("--probe-local", action="store_true")
+ args = parser.parse_args(argv)
+ try:
+ if args.command == "options":
+ result = build_catalog()
+ if args.dimension:
+ key = DIMENSIONS[args.dimension]
+ result = {k: result[k] for k in ("catalog_version", "schema_versions", key)}
+ if args.json:
+ print(json.dumps(result, indent=2, sort_keys=True))
+ else:
+ render_catalog(result)
+ return 0
+ if args.command == "validate":
+ result = validate_paths(args.config, args.deployment, check_files=args.check_files)
+ if args.json:
+ print(json.dumps(result, indent=2, sort_keys=True))
+ else:
+ render_validation(result)
+ return int(result["status"] == "invalid")
+ if not args.answers and not sys.stdin.isatty():
+ parser.error("init requires --answers when stdin is not a terminal")
+ output = Path(args.out_dir).expanduser().absolute()
+ if output.exists() or output.is_symlink():
+ raise ValueError(f"Output directory already exists: {output}")
+ if args.probe_local:
+ from .probe import probe_local
+ print(json.dumps(probe_local(output), indent=2))
+ answers = read_mapping(args.answers) if args.answers else interactive_answers()
+ bundle = build_config_bundle(answers, output_directory=output)
+ render_validation(bundle["validation"])
+ if not args.answers:
+ print(bundle["files"]["config.yaml"])
+ print(bundle["files"]["deployment.yaml"])
+ if ask("Publish these files? (yes/no)", "no").lower() != "yes":
+ print("Cancelled; no files written")
+ return 0
+ publish_bundle(bundle, output)
+ print(f"Created configuration bundle: {output}")
+ config = shlex.quote(str(output / "config.yaml"))
+ deployment = shlex.quote(str(output / "deployment.yaml"))
+ for command, suffix in (("validate", " --check-files"), ("train", " --dry-run"), ("doctor", "")):
+ print(f"harborrl {command} {config} --deployment {deployment}{suffix}")
+ print("doctor checks the live environment and connectivity; training requires a separate command.")
+ return 0
+ except (ValueError, TypeError, KeyError, AttributeError, OSError, yaml.YAMLError) as exc:
+ print(f"error: {exc}", file=sys.stderr)
+ return 1
+ except (EOFError, KeyboardInterrupt):
+ print("Cancelled; no configuration published", file=sys.stderr)
+ return 130
diff --git a/harborrl/options/presets.py b/harborrl/options/presets.py
new file mode 100644
index 000000000..6cc7ef8b5
--- /dev/null
+++ b/harborrl/options/presets.py
@@ -0,0 +1,153 @@
+"""Small configuration recipes; final validity belongs to existing schemas."""
+import sys
+from copy import deepcopy
+from pathlib import Path
+
+from harborrl.config import ROOT
+from harborrl.consistency.config import DEFAULTS as CONSISTENCY_DEFAULTS
+from harborrl.deployment.schema import destination, embedded_local
+
+PRESETS = (
+ {"id": "embedded-local", "description": "Local training and a local Harbor runner"},
+ {"id": "service-ssh", "description": "Controller, SSH GPU target and SSH Docker runners"},
+ {"id": "service-sandbox-local-runner", "description": "Controller and runner here; GPU target over SSH; Sandbox execution"},
+)
+
+
+def merge(base, override, path=""):
+ if not isinstance(override, dict):
+ raise TypeError(f"{path or 'answers'} must be a mapping")
+ result = deepcopy(base)
+ for key, value in override.items():
+ if key not in base:
+ raise ValueError(f"unknown field: {path}{key}")
+ result[key] = (merge(base[key], value, f"{path}{key}.")
+ if isinstance(base[key], dict) and base[key] else deepcopy(value))
+ return result
+
+
+def defaults(preset, output_directory):
+ if preset not in {item["id"] for item in PRESETS}:
+ raise ValueError(f"unknown preset: {preset}")
+ output = Path(output_directory).resolve()
+ service = preset != "embedded-local"
+ sandbox = preset == "service-sandbox-local-runner"
+ training = {
+ "consistency": deepcopy(CONSISTENCY_DEFAULTS),
+ "schema_version": 2,
+ "execution": {"backend": "harbor_job"}, "tasks": {"catalog": None},
+ "harness": {"name": "claude_code", "profile": None},
+ "harbor": {"python": None, "version": "0.23.0", "workers": [],
+ "runner_transport": "ssh" if preset == "service-ssh" else "local",
+ "environment": {"runtime": "sandbox", "config": None} if sandbox else {"runtime": "docker"}},
+ "gateway": {"host": "0.0.0.0" if service else "127.0.0.1", "port": 49184,
+ "advertised_url": None if service else "http://127.0.0.1:49184",
+ "tokenizer_digest": None, "template_digest": None, "audited_raw_logprobs": True},
+ "model": {"checkpoint": None, "reference": None, "args_file": "qwen3-8B"},
+ "training": {"num_rollout": 2, "learning_rate": 1e-6, "save_interval": 1,
+ "backend_contract": "slime-v0.3.2-native-v1"},
+ "sampling": {"group_size": 2, "groups_per_batch": 1, "max_attempts": 1,
+ "max_tokens": 2048, "max_context": 32768},
+ "deployment": {"layout": "colocate", "num_gpus": 1, "actor_gpus": 1,
+ "rollout_gpus": 1, "actor_tensor_parallel_size": 1, "rollout_gpus_per_engine": 1},
+ "output": {"root": str(output / "runs")},
+ }
+ deployment = embedded_local(run_root=str(output / "runs"))
+ deployment["gpu_target"]["environment_file"] = None
+ deployment["manager"]["run_token_file"] = str(output / "run-token")
+ if service:
+ deployment["manager"].update(id="controller", mode="service", bind="0.0.0.0",
+ control_url=None, gateway_url=None)
+ deployment["gpu_target"].update(id="gpu", transport="ssh", ssh={"host": None, "user": None, "port": None, "identity": None},
+ source_dir=None, train_python=None)
+ deployment["rollout"]["provider"] = "sandbox" if sandbox else "ssh_docker"
+ return training, deployment
+
+
+def build_configs(answers, output_directory):
+ if not isinstance(answers, dict) or set(answers) - {
+ "answers_version", "preset", "training", "deployment", "sandbox_config"}:
+ raise ValueError("unknown answers field or non-mapping answers")
+ if type(answers.get("answers_version")) is not int or answers["answers_version"] != 1:
+ raise ValueError("answers_version must be 1")
+ preset = answers.get("preset")
+ training, deployment = defaults(preset, output_directory)
+ t = answers.get("training", {})
+ d = answers.get("deployment", {})
+ training = merge(training, t, "training.")
+ deployment = merge(deployment, d, "deployment.")
+
+ # Repeated fields have one source unless both were explicitly supplied.
+ for section, field, other_section, other_field in (
+ ("gateway", "host", "manager", "bind"),
+ ("gateway", "port", "manager", "gateway_port"),
+ ("gateway", "advertised_url", "manager", "gateway_url"),
+ ("output", "root", "storage", "run_root"),
+ ):
+ if field in t.get(section, {}) and other_field not in d.get(other_section, {}):
+ deployment[other_section][other_field] = training[section][field]
+ elif other_field in d.get(other_section, {}) and field not in t.get(section, {}):
+ training[section][field] = deployment[other_section][other_field]
+ elif field in t.get(section, {}) and other_field in d.get(other_section, {}):
+ left, right = training[section][field], deployment[other_section][other_field]
+ if field == "root" and isinstance(left, str) and isinstance(right, str):
+ left, right = ((Path(output_directory) / value).resolve() for value in (left, right))
+ elif field == "advertised_url" and isinstance(left, str) and isinstance(right, str):
+ left, right = left.rstrip("/"), right.rstrip("/")
+ if left != right:
+ raise ValueError(f"training.{section}.{field} conflicts with deployment.{other_section}.{other_field}")
+ count = training["deployment"]["num_gpus"]
+ if "expected_gpus" not in d.get("gpu_target", {}):
+ deployment["gpu_target"]["expected_gpus"] = count
+ if training["deployment"]["layout"] == "colocate":
+ for field in ("actor_gpus", "rollout_gpus"):
+ if field not in t.get("deployment", {}):
+ training["deployment"][field] = count
+ if training["harbor"]["runner_transport"] == "ssh":
+ workers = deployment["rollout"]["workers"]
+ if workers:
+ if "workers" not in t.get("harbor", {}):
+ training["harbor"]["workers"] = [destination(w["ssh"]) for w in workers]
+ if "python" not in t.get("harbor", {}):
+ training["harbor"]["python"] = workers[0]["runner_python"]
+ if answers.get("sandbox_config") is not None:
+ if preset != "service-sandbox-local-runner":
+ raise ValueError("sandbox_config requires the Sandbox preset")
+ environment = training["harbor"]["environment"]
+ if environment.get("config") not in (None, answers["sandbox_config"]):
+ raise ValueError("sandbox_config conflicts with training.harbor.environment.config")
+ environment["config"] = answers["sandbox_config"]
+ required = [("training", section, field) for section, field in (
+ ("tasks", "catalog"), ("harness", "profile"), ("harbor", "python"),
+ ("model", "checkpoint"), ("model", "reference"),
+ ("gateway", "tokenizer_digest"), ("gateway", "template_digest"), ("gateway", "advertised_url"))]
+ if preset != "embedded-local":
+ required += [("deployment", section, field) for section, field in (
+ ("manager", "control_url"), ("manager", "gateway_url"),
+ ("gpu_target", "source_dir"), ("gpu_target", "train_python"))]
+ configs = {"training": training, "deployment": deployment}
+ missing = [".".join(keys) for keys in required if configs[keys[0]][keys[1]][keys[2]] in (None, "")]
+ if preset != "embedded-local" and not deployment["gpu_target"]["ssh"].get("host"):
+ missing.append("deployment.gpu_target.ssh.host")
+ if preset == "service-ssh" and not deployment["rollout"]["workers"]:
+ missing.append("deployment.rollout.workers")
+ if preset == "service-sandbox-local-runner" and not training["harbor"]["environment"].get("config"):
+ missing.append("sandbox_config")
+ if missing:
+ raise ValueError("Missing required answers: " + ", ".join(missing))
+ for field in ("tokenizer_digest", "template_digest"):
+ if training["gateway"][field] == "0" * 64:
+ raise ValueError(f"gateway.{field}: a placeholder digest cannot be used by init")
+ expected_mode = "embedded" if preset == "embedded-local" else "service"
+ expected_provider = {"embedded-local": "embedded", "service-ssh": "ssh_docker",
+ "service-sandbox-local-runner": "sandbox"}[preset]
+ if deployment["manager"]["mode"] != expected_mode or deployment["rollout"]["provider"] != expected_provider:
+ raise ValueError("deployment mode/provider conflicts with the selected preset")
+ # All generated path references are rooted at the final directory, not staging.
+ deployment["storage"]["run_root"] = str((Path(output_directory) / deployment["storage"]["run_root"]).resolve())
+ return training, deployment
+
+
+def worker_defaults():
+ return {"id": "worker-0", "ssh": {"host": None}, "source_dir": str(ROOT),
+ "runner_python": sys.executable, "docker": True, "task_roots": [str(ROOT)]}
diff --git a/harborrl/options/probe.py b/harborrl/options/probe.py
new file mode 100644
index 000000000..12131c5df
--- /dev/null
+++ b/harborrl/options/probe.py
@@ -0,0 +1,39 @@
+"""Explicit, bounded local hints. Never connect to a remote machine."""
+import os
+import shlex
+import subprocess
+import sys
+from pathlib import Path
+
+
+def command_hint(command):
+ try:
+ result = subprocess.run(command, capture_output=True, text=True, timeout=3, check=True)
+ return {"method": command[0], "status": "observed", "value": result.stdout.strip(), "reason": None}
+ except (OSError, subprocess.SubprocessError) as exc:
+ return {"method": command[0], "status": "unknown", "value": None, "reason": str(exc)}
+
+
+def ssh_aliases(path):
+ aliases = set()
+ for line in Path(path).read_text(encoding="utf-8").splitlines():
+ words = shlex.split(line, comments=True)
+ if words and words[0].lower() == "host":
+ aliases.update(word for word in words[1:] if not any(c in word for c in "*?!"))
+ return sorted(aliases)
+
+
+def probe_local(output_directory):
+ try:
+ aliases = {"method": "ssh_config_hosts", "status": "observed",
+ "value": ssh_aliases(Path.home() / ".ssh/config"), "reason": "Include and Match are not evaluated"}
+ except (OSError, ValueError) as exc:
+ aliases = {"method": "ssh_config_hosts", "status": "unknown", "value": None, "reason": str(exc)}
+ parent = Path(output_directory).expanduser().absolute().parent
+ return {
+ "gpu": command_hint(["nvidia-smi", "--query-gpu=uuid", "--format=csv,noheader"]),
+ "python": command_hint([sys.executable, "-I", "--version"]),
+ "ssh_aliases": aliases,
+ "output_parent": {"method": "os.access", "status": "hint", "value": os.access(parent, os.W_OK),
+ "reason": "Access hint only; no write was attempted"},
+ }
diff --git a/harborrl/options/resources/datasets.yaml b/harborrl/options/resources/datasets.yaml
new file mode 100644
index 000000000..40d8e6726
--- /dev/null
+++ b/harborrl/options/resources/datasets.yaml
@@ -0,0 +1,40 @@
+schema_version: 1
+datasets:
+ - name: hello_world
+ status: example
+ source: bundled
+ upstream_revision: v0.1-example
+ license: MIT
+ task_root: examples/native/tasks
+ expected_task_count: 1
+ digest_policy: native-inspector-tree-sha256
+ docker_base: debian:bookworm-slim
+ gpu_required: false
+ acceptance: offline-native-contract-only
+ owner: HarborRL maintainers
+
+ - name: terminal-bench-core
+ status: planned
+ source: https://github.com/laude-institute/terminal-bench
+ upstream_revision: TBD
+ license: TBD
+ task_root: TBD
+ expected_task_count: TBD
+ digest_policy: native-inspector-tree-sha256
+ docker_base: task-defined
+ gpu_required: false
+ acceptance: pending-public-worker-matrix
+ owner: HarborRL maintainers
+
+ - name: terminal-bench-test
+ status: planned
+ source: https://github.com/laude-institute/terminal-bench
+ upstream_revision: dataset/terminal-bench-core/v0.1.x
+ license: TBD
+ task_root: TBD
+ expected_task_count: TBD
+ digest_policy: native-inspector-tree-sha256
+ docker_base: task-defined
+ gpu_required: false
+ acceptance: pending-public-worker-matrix
+ owner: HarborRL maintainers
diff --git a/harborrl/options/validation.py b/harborrl/options/validation.py
new file mode 100644
index 000000000..53465d83e
--- /dev/null
+++ b/harborrl/options/validation.py
@@ -0,0 +1,169 @@
+"""Offline checks composed from the authoritative configuration validators."""
+from copy import deepcopy
+from pathlib import Path
+
+import yaml
+
+from harborrl.config import native
+from harborrl.deployment import schema
+
+
+def read_mapping(path):
+ value = yaml.safe_load(Path(path).read_text(encoding="utf-8"))
+ if not isinstance(value, dict):
+ raise TypeError("configuration must be a mapping")
+ return value
+
+
+def check(path, status, code, message):
+ return {"path": path, "status": status, "code": code, "message": message}
+
+
+def report(checks, *, check_files=False, source="explicit"):
+ states = {item["status"] for item in checks}
+ status = "invalid" if "failed" in states else (
+ "valid_with_deferred_checks" if "deferred" in states else "valid")
+ return {"validation_version": 1, "status": status,
+ "scope": "structure_and_local_files" if check_files else "structure",
+ "deployment_source": source, "checks": checks}
+
+
+def cross_checks(training, deployment):
+ """Only compare settings actually consumed together by the selected mode."""
+ checks = []
+
+ def require(condition, path, message):
+ if not condition:
+ checks.append(check(path, "failed", "inconsistent", message))
+
+ gpu = deployment["gpu_target"]
+ require(training["deployment"]["num_gpus"] <= gpu["expected_gpus"],
+ "deployment.gpu_target.expected_gpus", "Expected GPU count is below the training budget")
+ if deployment["manager"]["mode"] != "service":
+ return checks
+ manager, rollout, harbor = deployment["manager"], deployment["rollout"], training["harbor"]
+ for key, other in (("host", "bind"), ("port", "gateway_port"), ("advertised_url", "gateway_url")):
+ left, right = training["gateway"][key], manager[other]
+ if key == "advertised_url":
+ left, right = left.rstrip("/"), right.rstrip("/")
+ require(left == right, f"training.gateway.{key}", f"Must match deployment.manager.{other}")
+ require(training["output"]["root"] == deployment["storage"]["run_root"],
+ "training.output.root", "Must match deployment.storage.run_root")
+ sandbox = harbor.get("environment", {}).get("runtime") == "sandbox"
+ require(sandbox == (rollout["provider"] == "sandbox"), "deployment.rollout.provider",
+ "Sandbox provider and training environment must agree")
+ workers = rollout["workers"]
+ local = harbor.get("runner_transport", "ssh") == "local"
+ require(not workers if local else bool(workers), "deployment.rollout.workers",
+ "Local runners require no workers; SSH runners require workers")
+ if not local and workers:
+ destinations = [schema.destination(worker["ssh"]) for worker in workers]
+ require(harbor["workers"] == destinations, "training.harbor.workers",
+ "Must match deployment worker destinations in order")
+ require(harbor["python"] == workers[0]["runner_python"], "training.harbor.python",
+ "Must match the deployment's first runner_python")
+ return checks
+
+
+def _local_file(checks, path, label, *, directory=False):
+ target = Path(path)
+ exists = target.is_dir() if directory else target.is_file()
+ checks.append(check(label, "passed" if exists else "failed", "local_path",
+ "Local path exists" if exists else "Required local path is missing"))
+
+
+def _file_checks(training, deployment):
+ checks = []
+ manager = deployment["manager"]
+ _local_file(checks, manager["source_dir"], "deployment.manager.source_dir", directory=True)
+ _local_file(checks, manager["controller_python"], "deployment.manager.controller_python")
+ for label, function in (
+ ("training.harness.profile", lambda: native.validate_files(training)),
+ ("training.tasks.catalog", lambda: native.catalog(training)),
+ ):
+ try:
+ function()
+ except (ValueError, TypeError, KeyError, OSError, AttributeError) as exc:
+ checks.append(check(label, "failed", "local_reference", str(exc)))
+ else:
+ checks.append(check(label, "passed", "local_reference", "Local reference validated"))
+ gpu = deployment["gpu_target"]
+ if gpu["transport"] == "local":
+ for key in ("checkpoint", "reference"):
+ _local_file(checks, training["model"][key], f"training.model.{key}", directory=True)
+ _local_file(checks, gpu["train_python"], "deployment.gpu_target.train_python")
+ _local_file(checks, gpu["source_dir"], "deployment.gpu_target.source_dir", directory=True)
+ if gpu.get("environment_file"):
+ _local_file(checks, gpu["environment_file"], "deployment.gpu_target.environment_file")
+ else:
+ checks.append(check("deployment.gpu_target", "deferred", "remote_paths",
+ "Model, interpreter and source paths belong to the GPU target"))
+ if training["harbor"].get("runner_transport", "ssh") == "local":
+ _local_file(checks, training["harbor"]["python"], "training.harbor.python")
+ else:
+ checks.append(check("deployment.rollout.workers", "deferred", "remote_paths",
+ "Runner paths must be checked on their worker machines"))
+ environment = training["harbor"].get("environment", {})
+ if environment.get("runtime") == "sandbox":
+ from harborrl.rollout.harbor_job.sandbox_config import (
+ load_sandbox,
+ task_binding,
+ )
+ try:
+ cfg = load_sandbox(environment["config"])
+ if cfg["adapter"] == "harborrl.rollout.harbor_job.environments.opensandbox:ManagedOpenSandboxEnvironment":
+ if cfg["options"] or any(binding["options"] for binding in cfg["bindings"]):
+ raise ValueError("standard OpenSandbox does not accept platform options")
+ else:
+ checks.append(check("sandbox.adapter", "deferred", "runner_adapter",
+ "Platform adapter validation belongs to its trusted runner environment"))
+ for entry in native.catalog(training):
+ task_binding(cfg, entry["path"])
+ except (ValueError, TypeError, KeyError, OSError, AttributeError, yaml.YAMLError) as exc:
+ checks.append(check("training.harbor.environment", "failed", "sandbox_config", str(exc)))
+ else:
+ checks.append(check("training.harbor.environment", "passed", "sandbox_config",
+ "Sandbox structure and task image bindings validated; credentials not read"))
+ checks.append(check("sandbox.platform", "deferred", "platform_access",
+ "Credentials, offline binary, capacity and network need runner preflight"))
+ return checks
+
+
+def validate_mappings(training, deployment, *, config_path, check_files=False, source="explicit"):
+ checks = []
+ normalized = {}
+ for name, function in (
+ ("training", lambda: native.validate(deepcopy(training), Path(config_path).resolve())),
+ ("deployment", lambda: schema.validate(deepcopy(deployment))),
+ ):
+ try:
+ normalized[name] = function()
+ except (ValueError, TypeError, KeyError, OSError, AttributeError) as exc:
+ checks.append(check(name, "failed", "schema", str(exc)))
+ else:
+ checks.append(check(name, "passed", "schema", "Schema and semantic checks passed"))
+ if len(normalized) == 2:
+ checks.extend(cross_checks(normalized["training"], normalized["deployment"]))
+ if check_files:
+ checks.extend(_file_checks(normalized["training"], normalized["deployment"]))
+ else:
+ checks.append(check("references", "deferred", "files_not_checked", "Use --check-files for local references"))
+ checks.append(check("runtime", "deferred", "live_preflight",
+ "GPU memory, model loading, live digests and connectivity require doctor or an explicit live test"))
+ return report(checks, check_files=check_files, source=source)
+
+
+def validate_paths(config_path, deployment_path=None, *, check_files=False):
+ try:
+ training = read_mapping(config_path)
+ deployment, source = schema.load_deployment(deployment_path)
+ if source == "embedded-local":
+ normalized = native.validate(deepcopy(training), Path(config_path).resolve())
+ deployment = schema.embedded_local(run_root=normalized["output"]["root"],
+ workers=normalized["harbor"]["workers"], expected_gpus=normalized["deployment"]["num_gpus"])
+ # The legacy validator omits an optional null key from its normalized output.
+ deployment["gpu_target"].setdefault("environment_file", None)
+ return validate_mappings(training, deployment, config_path=config_path,
+ check_files=check_files, source=source)
+ except (ValueError, TypeError, KeyError, OSError, AttributeError, yaml.YAMLError) as exc:
+ return report([check("configuration", "failed", "load", str(exc))], check_files=check_files)
diff --git a/harborrl/options/wizard.py b/harborrl/options/wizard.py
new file mode 100644
index 000000000..55cba4a53
--- /dev/null
+++ b/harborrl/options/wizard.py
@@ -0,0 +1,68 @@
+"""Build a reviewed configuration bundle, then publish it to a new directory."""
+import json
+import os
+import shutil
+import tempfile
+from pathlib import Path
+
+import yaml
+
+from harborrl.config.native import validate
+from harborrl.trajectories.native import digest
+
+from .presets import build_configs
+from .validation import validate_mappings
+
+
+def build_config_bundle(answers, *, output_directory):
+ output = Path(output_directory).expanduser().resolve()
+ training, deployment = build_configs(answers, output)
+ result = validate_mappings(training, deployment, config_path=output / "config.yaml")
+ if result["status"] == "invalid":
+ raise ValueError("; ".join(f"{row['path']}: {row['message']}" for row in result["checks"] if row["status"] == "failed"))
+ training = validate(training, output / "config.yaml")
+ manifest = {"setup_version": 1, "preset": answers["preset"],
+ "schema_versions": {"training": 2, "deployment": 1},
+ "config_digest": digest(training), "deployment_digest": digest(deployment),
+ "references": {"catalog": training["tasks"]["catalog"],
+ "profile": training["harness"]["profile"],
+ "sandbox_config": training["harbor"].get("environment", {}).get("config")},
+ "validation": result}
+ return {"files": {"config.yaml": yaml.safe_dump(training, sort_keys=False),
+ "deployment.yaml": yaml.safe_dump(deployment, sort_keys=False),
+ "setup-manifest.json": json.dumps(manifest, indent=2) + "\n"},
+ "validation": result}
+
+
+def publish_bundle(bundle, output_directory):
+ """No overwrite; same-parent staging and an exclusive per-target lock."""
+ output = Path(output_directory).expanduser().absolute()
+ if output.exists() or output.is_symlink():
+ raise FileExistsError(f"Output directory already exists: {output}")
+ if not output.parent.is_dir():
+ raise ValueError("Output parent directory must already exist")
+ files = bundle["files"]
+ if set(files) != {"config.yaml", "deployment.yaml", "setup-manifest.json"}:
+ raise ValueError("Bundle must contain exactly the three documented files")
+ lock = output.parent / f".{output.name}.init.lock"
+ descriptor = os.open(lock, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
+ staging = None
+ try:
+ os.close(descriptor)
+ if output.exists() or output.is_symlink():
+ raise FileExistsError(f"Output directory already exists: {output}")
+ staging = Path(tempfile.mkdtemp(prefix=f".{output.name}.init-", dir=output.parent))
+ for name, content in files.items():
+ with (staging / name).open("x", encoding="utf-8") as stream:
+ stream.write(content)
+ stream.flush()
+ os.fsync(stream.fileno())
+ if output.exists() or output.is_symlink():
+ raise FileExistsError(f"Output directory already exists: {output}")
+ staging.rename(output)
+ staging = None
+ finally:
+ if staging is not None:
+ shutil.rmtree(staging)
+ lock.unlink()
+ return output
diff --git a/harborrl/platform/evidence.py b/harborrl/platform/evidence.py
new file mode 100644
index 000000000..d988cf86f
--- /dev/null
+++ b/harborrl/platform/evidence.py
@@ -0,0 +1,126 @@
+"""Run input preparation and reproducible source evidence."""
+import hashlib
+import json
+import subprocess
+import sys
+import time
+from pathlib import Path
+from uuid import uuid4
+
+from harborrl.config import ROOT
+from harborrl.trajectories.native import publish
+
+
+def new_run_root(output_root):
+ name = time.strftime("%Y%m%d-%H%M%S") + "-" + uuid4().hex[:8]
+ return Path(output_root) / "training" / name
+
+
+def run_environment(plan, root, catalog_path, prompt_path):
+ """Shared run paths for both local and controller-managed training."""
+ return {
+ **plan["environment"],
+ "RUN_ID": root.name, "RUN_NAME": root.name, "RUN_DIR": str(root),
+ "RUNS_ROOT": str(root.parent.parent.resolve()),
+ "CKPT_ROOT": str(root / "checkpoints"), "WANDB_DIR": str(root / "metrics/wandb"),
+ "HARBORRL_NATIVE_CATALOG": str(catalog_path),
+ "ROLLOUT_PROMPT_DATA": str(prompt_path),
+ "HARBORRL_NATIVE_PROFILE": plan["config"]["harness"]["profile"],
+ "HARBOR_IR_ROOT": str(root / "ir"),
+ }
+
+
+def _materialize(plan, root):
+ from harborrl.math_registry import select_math
+
+ config = plan["config"]
+ entries = select_math(config).task.adapter.catalog(config)
+ normalized = root / "config"
+ normalized.mkdir(parents=True)
+ catalog_path = normalized / "native_catalog.json"
+ publish(catalog_path, entries)
+ prompt_path = normalized / "native_tasks.jsonl"
+ rows = select_math(config).task.adapter.prompt_rows(entries)
+ prompt_path.write_text("".join(json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n" for row in rows))
+ return catalog_path, prompt_path
+
+
+def _source_audit(root):
+ def git(*args):
+ return subprocess.check_output(["git", *args], cwd=ROOT)
+
+ files = git("ls-files", "-z", "--cached", "--others", "--exclude-standard", "--",
+ "harborrl", "scripts", "examples", "pyproject.toml").decode().split("\0")
+ untracked = set(git("ls-files", "-z", "--others", "--exclude-standard", "--",
+ "harborrl", "scripts", "examples").decode().split("\0"))
+ (root / "source.patch").write_bytes(git("diff", "--binary", "HEAD", "--",
+ "harborrl", "scripts", "examples", "pyproject.toml"))
+ hashes = {}
+ for name in files:
+ source = ROOT / name
+ if name and source.is_file():
+ hashes[name] = hashlib.sha256(source.read_bytes()).hexdigest()
+ if name in untracked:
+ target = root / "source-new" / name
+ target.parent.mkdir(parents=True, exist_ok=True)
+ target.write_bytes(source.read_bytes())
+ payload = {
+ "commit": git("rev-parse", "HEAD").decode().strip(),
+ "dirty": bool(git("status", "--porcelain")),
+ "source_hashes": hashes,
+ "patch": "source.patch",
+ }
+ publish(root / "source-manifest.json", payload)
+ return payload
+
+
+
+
+# Proxy values and provider credentials are deliberately outside this snapshot.
+DEPLOYMENT_EVIDENCE_KEYS = ("SLIME_DIR", "MEGATRON_DIR", "SGLANG_IMAGE", "CUDA_VISIBLE_DEVICES")
+
+
+def effective_config(plan, environment, child_env, source, checks):
+ from harborrl.backends.slime_v032.config import field_usage
+ config = plan["config"]
+ return {
+ "schema_version": "harborrl-effective-config-v1",
+ "components": {
+ "model_preset": config["model"]["args_file"],
+ "algorithm": plan["math"]["algorithm"]["id"],
+ "harness": plan["math"]["harness"]["id"],
+ "execution_backend": config["execution"]["backend"],
+ "training_backend_contract": config["training"]["backend_contract"],
+ },
+ "environment": environment,
+ "deployment_environment": {key: child_env[key] for key in DEPLOYMENT_EVIDENCE_KEYS if key in child_env},
+ "training_command": plan["training_command"],
+ "expanded_command_file": "effective-command.json",
+ "source": source,
+ "runtime_versions": [check for check in checks
+ if "version" in check or check.get("service") in
+ ("slime commit", "megatron commit", "sglang image")],
+ "field_usage": field_usage(),
+ "precedence": "YAML < --set in order < explicit MATH selectors; declared deployment environment only",
+ }
+
+
+def record_command(root, command, *, dry_run=False):
+ """Record argv after shell defaults and model preset expansion."""
+ payload = {
+ "schema_version": "harborrl-effective-command-v1",
+ "command": command,
+ "mode": "dry_run" if dry_run else "training",
+ }
+ target = Path(root) / "effective-command.json"
+ if dry_run:
+ checksum = hashlib.sha256(json.dumps(command).encode()).hexdigest()
+ target = Path(root) / "dry-run-commands" / f"{checksum}.json"
+ publish(target, payload)
+
+
+if __name__ == "__main__":
+ # Internal shell boundary: RUN_DIR, mode, then the exact executable argv.
+ if len(sys.argv) < 4 or sys.argv[2] not in ("0", "1"):
+ raise SystemExit("usage: evidence RUN_DIR DRY_RUN COMMAND [ARG ...]")
+ record_command(sys.argv[1], sys.argv[3:], dry_run=sys.argv[2] == "1")
diff --git a/harborrl/platform/native_train.py b/harborrl/platform/native_train.py
index a2a356dbc..0c0eedba2 100644
--- a/harborrl/platform/native_train.py
+++ b/harborrl/platform/native_train.py
@@ -2,249 +2,50 @@
The dispatcher freezes a run directory and materializes the original-task lock
into Slime prompt rows before handing lifecycle control to the existing Slime
-launcher. It never rewrites task tests or rewards.
+launcher. It never rewrites task tests or rewards. A service-mode deployment
+routes through the manager-side controller instead of a local child process.
"""
from __future__ import annotations
-import hashlib
-import importlib
-import importlib.metadata
import json
-import os
-import shutil
-import signal
-import subprocess
import sys
-import time
from pathlib import Path
-from types import SimpleNamespace
-from uuid import uuid4
-from harborrl.config import ROOT
+from harborrl.deployment.training_target import local_target
+from harborrl.platform.evidence import effective_config, new_run_root, run_environment
from harborrl.trajectories.native import publish, read_json
def _run_root(plan):
- name = time.strftime("%Y%m%d-%H%M%S") + "-" + uuid4().hex[:8]
- return Path(plan["config"]["output"]["root"]) / "training" / name
+ return new_run_root(plan["config"]["output"]["root"])
def doctor(plan):
- config = plan["config"]
- checks = []
-
- runner = Path(config["harbor"]["python"])
- checks.append({"path": "harbor.python", "ok": runner.is_file()})
- from harborrl.rollout.harbor_job.coordinator import runner_command
- if runner.is_file():
- for worker in config["harbor"]["workers"]:
- try:
- probe = subprocess.run(
- runner_command(str(runner), ROOT, worker) + ["--probe"],
- capture_output=True, text=True, timeout=30, check=False,
- )
- except (subprocess.SubprocessError, OSError) as exc:
- probe = SimpleNamespace(returncode=1, stdout="", stderr=str(exc))
- try:
- payload = json.loads(probe.stdout)
- runtime_version = tuple(payload.get("python", "").split(".")[:2])
- version_ok = payload.get("harbor_version") == config["harbor"]["version"]
- runtime_ok = len(runtime_version) == 2 and all(part.isdigit() for part in runtime_version)
- except (ValueError, AttributeError, TypeError):
- runtime_version, version_ok, runtime_ok = (), False, False
- checks.append({
- "service": "external Harbor worker",
- "worker": worker,
- "ok": probe.returncode == 0 and runtime_ok
- and tuple(map(int, runtime_version)) >= (3, 12) and version_ok,
- "detail": probe.stdout.strip() or probe.stderr.strip()[-1000:],
- })
-
- for key, path in (("tasks.catalog", config["tasks"]["catalog"]),
- ("harness.profile", config["harness"]["profile"]),
- ("model.checkpoint", config["model"]["checkpoint"]),
- ("model.reference", config["model"]["reference"])):
- checks.append({"path": key, "ok": Path(path).exists()})
-
- model_config = Path(config["model"]["checkpoint"]) / "config.json"
- if model_config.is_file():
- try:
- model = json.loads(model_config.read_text())
- heads = model.get("num_attention_heads", 0)
- kv_heads = model.get("num_key_value_heads", heads)
- for tp in {config["deployment"]["actor_tensor_parallel_size"],
- config["deployment"]["rollout_gpus_per_engine"]}:
- checks.append({"model_tp": tp, "ok": bool(
- heads and kv_heads and heads % tp == 0 and (kv_heads % tp == 0 or tp % kv_heads == 0))})
- except (OSError, ValueError, TypeError) as exc:
- checks.append({"path": "model.config", "ok": False, "detail": str(exc)})
-
- from harborrl.config.native import catalog
- try:
- entries = catalog(config)
- for entry in entries:
- from harborrl.data.harbor.native_inspector import inspect_native
- report = inspect_native(entry["path"], expected_digest=entry["task_digest"])
- checks.append({"task": entry["id"], "ok": report["status"] == "NEEDS_PROBE",
- "detail": report["reasons"]})
- except (ValueError, OSError, TypeError) as exc:
- checks.append({"service": "native catalog", "ok": False, "detail": str(exc)})
-
- for package in ("ray", "torch", "sglang", "transformers"):
- try:
- version = importlib.metadata.version(package)
- except importlib.metadata.PackageNotFoundError:
- version = None
- checks.append({"dependency": package, "version": version, "ok": version is not None})
-
- from harborrl.backends.slime_v032.versions import (
- MEGATRON_COMMIT,
- SGLANG_IMAGE_TAG,
- SLIME_COMMIT,
- )
- slime_dir = os.environ.get("SLIME_DIR", "").strip()
- slime_path = Path(slime_dir) if slime_dir else None
- checks.append({"path": "SLIME_DIR", "ok": bool(slime_path and slime_path.is_dir()),
- "detail": slime_dir or "not set"})
- if slime_path and slime_path.is_dir():
- checks.append({"path": "slime entrypoint", "ok": (slime_path / "train.py").is_file()})
- preset = slime_path / "scripts" / "models" / f"{config['model']['args_file']}.sh"
- checks.append({"path": "slime model preset", "ok": preset.is_file(), "detail": str(preset)})
- try:
- commit = subprocess.run(["git", "-C", str(slime_path), "rev-parse", "HEAD"],
- capture_output=True, text=True, timeout=15, check=True).stdout.strip()
- except (subprocess.SubprocessError, OSError) as exc:
- commit = ""
- checks.append({"service": "slime commit", "ok": False, "detail": str(exc)})
- if commit:
- checks.append({"service": "slime commit", "ok": commit == SLIME_COMMIT,
- "detail": f"{commit} != {SLIME_COMMIT}" if commit != SLIME_COMMIT else commit})
-
- megatron_dir = os.environ.get("MEGATRON_DIR", "").strip()
- if megatron_dir:
- try:
- megatron_commit = subprocess.run(["git", "-C", megatron_dir, "rev-parse", "HEAD"],
- capture_output=True, text=True, timeout=15, check=True).stdout.strip()
- except (subprocess.SubprocessError, OSError) as exc:
- megatron_commit = ""
- checks.append({"service": "megatron commit", "ok": False, "detail": str(exc)})
- if megatron_commit:
- checks.append({"service": "megatron commit", "ok": megatron_commit == MEGATRON_COMMIT,
- "detail": f"{megatron_commit} != {MEGATRON_COMMIT}" if megatron_commit != MEGATRON_COMMIT else megatron_commit})
- else:
- checks.append({"service": "megatron commit",
- "ok": True,
- "detail": "MEGATRON_DIR not set; official Slime image bundles Megatron"})
-
- runtime_env = os.environ.copy()
- runtime_pythonpath = os.pathsep.join(filter(None, [
- str(slime_path), megatron_dir, str(ROOT), runtime_env.get("PYTHONPATH", ""),
- ]))
- runtime_env["PYTHONPATH"] = runtime_pythonpath
- for module in ("slime.rollout.base_types", "slime.utils.types",
- "slime.backends.megatron_utils.loss"):
- try:
- probe = subprocess.run([sys.executable, "-c", f"import {module}"],
- capture_output=True, text=True, timeout=90, check=False,
- env=runtime_env)
- except (subprocess.SubprocessError, OSError) as exc:
- probe = SimpleNamespace(returncode=1, stdout="", stderr=str(exc))
- checks.append({"dependency": module, "ok": probe.returncode == 0,
- "detail": probe.stderr.strip()[-1000:]})
-
- sglang_image = os.environ.get("SGLANG_IMAGE", "").strip()
- if sglang_image:
- checks.append({"service": "sglang image", "ok": sglang_image.endswith(SGLANG_IMAGE_TAG),
- "detail": sglang_image})
-
- if shutil.which("nvidia-smi"):
- try:
- result = subprocess.run(["nvidia-smi", "--query-gpu=index", "--format=csv,noheader"],
- capture_output=True, text=True, timeout=10, check=True)
- visible = os.environ.get("CUDA_VISIBLE_DEVICES")
- count = (len([item for item in visible.split(",") if item.strip() and item.strip() != "-1"])
- if visible is not None else len(result.stdout.splitlines()))
- checks.append({"resource": "visible GPU budget", "available": count,
- "ok": count >= config["deployment"]["num_gpus"]})
- except (subprocess.SubprocessError, OSError) as exc:
- checks.append({"resource": "visible GPU budget", "ok": False, "detail": str(exc)})
- else:
- checks.append({"resource": "visible GPU budget", "ok": False, "detail": "nvidia-smi unavailable"})
-
- if config["training"]["backend_contract"] != "slime-legacy":
- from harborrl.backends.slime_v032.launcher import hook_modules
- for hook_name, module_name in hook_modules().items():
- try:
- importlib.import_module(module_name)
- checks.append({"service": "training backend hook", "hook": hook_name, "ok": True})
- except (ImportError, AttributeError, TypeError, ValueError) as exc:
- checks.append({
- "service": "training backend hook", "hook": hook_name,
- "ok": False, "detail": str(exc),
- })
-
- checks.append({
- "service": "raw-model logprob audit",
- "ok": True,
- "detail": ("enabled by lock; live serving semantic probe remains a GPU gate"
- if config["gateway"]["audited_raw_logprobs"] else
- "disabled; generated evidence remains EVAL_ONLY and training will fail closed"),
- })
- return checks
+ """Compatibility entry point for deployment preflight."""
+ from harborrl.platform.preflight import doctor as check
+ return check(plan)
def _materialize(plan, root):
- from harborrl.config.native import catalog
-
- config = plan["config"]
- entries = catalog(config)
- normalized = root / "config"
- normalized.mkdir(parents=True)
- catalog_path = normalized / "native_catalog.json"
- publish(catalog_path, entries)
- prompt_path = normalized / "native_tasks.jsonl"
- rows = []
- for entry in entries:
- instruction = (Path(entry["path"]) / "instruction.md").read_text()
- rows.append({"task": instruction, "metadata": {"harborrl_native": True, "native_task": entry}})
- prompt_path.write_text("".join(json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n" for row in rows))
- return catalog_path, prompt_path
+ from harborrl.platform.evidence import _materialize as prepare
+ return prepare(plan, root)
def _source_audit(root):
- def git(*args):
- return subprocess.check_output(["git", *args], cwd=ROOT)
-
- files = git("ls-files", "-z", "--cached", "--others", "--exclude-standard", "--",
- "harborrl", "scripts", "examples", "pyproject.toml").decode().split("\0")
- untracked = set(git("ls-files", "-z", "--others", "--exclude-standard", "--",
- "harborrl", "scripts", "examples").decode().split("\0"))
- (root / "source.patch").write_bytes(git("diff", "--binary", "HEAD", "--",
- "harborrl", "scripts", "examples", "pyproject.toml"))
- hashes = {}
- for name in files:
- source = ROOT / name
- if name and source.is_file():
- hashes[name] = hashlib.sha256(source.read_bytes()).hexdigest()
- if name in untracked:
- target = root / "source-new" / name
- target.parent.mkdir(parents=True, exist_ok=True)
- target.write_bytes(source.read_bytes())
- payload = {
- "commit": git("rev-parse", "HEAD").decode().strip(),
- "dirty": bool(git("status", "--porcelain")),
- "source_hashes": hashes,
- "patch": "source.patch",
- }
- publish(root / "source-manifest.json", payload)
- return payload
+ from harborrl.platform.evidence import _source_audit as audit
+ return audit(root)
def dispatch(plan, *, doctor_only=False):
+ deployment = (plan.get("deployment") or {}).get("config")
if doctor_only:
checks = doctor(plan)
+ explicit = plan.get("deployment", {}).get("source", "embedded-local") != "embedded-local"
print(json.dumps({"checks": checks,
- "scope": "native config, original tasks, isolated Harbor, training dependencies and GPU budget; protocol/token semantics require the deferred live probe"},
+ "scope": ("deployment topology, " if explicit else "")
+ + "native config, original tasks, isolated Harbor, training "
+ "dependencies and GPU budget; protocol/token semantics "
+ "require the deferred live probe"},
indent=2))
return int(any(not check["ok"] for check in checks))
@@ -254,51 +55,23 @@ def dispatch(plan, *, doctor_only=False):
print(json.dumps({"failed_checks": failed}, indent=2), file=sys.stderr)
return 1
+ if deployment is not None and deployment["manager"]["mode"] == "service":
+ from harborrl.deployment.controller import dispatch_service
+ return dispatch_service(plan, deployment, checks=checks)
+
root = _run_root(plan)
root.mkdir(parents=True)
catalog_path, prompt_path = _materialize(plan, root)
source = _source_audit(root)
- environment = dict(plan["environment"])
- environment.update({
- "RUN_ID": root.name, "RUN_NAME": root.name, "RUN_DIR": str(root),
- "RUNS_ROOT": str(Path(plan["config"]["output"]["root"]).resolve()),
- "CKPT_ROOT": str(root / "checkpoints"), "WANDB_DIR": str(root / "metrics/wandb"),
- "HARBORRL_NATIVE_CATALOG": str(catalog_path),
- "ROLLOUT_PROMPT_DATA": str(prompt_path),
- "HARBORRL_NATIVE_PROFILE": plan["config"]["harness"]["profile"],
- "HARBOR_IR_ROOT": str(root / "ir"),
- })
+ environment = run_environment(plan, root, catalog_path, prompt_path)
launch = {"plan": plan, "run_dir": str(root), "source": source,
"materialized_catalog": str(catalog_path), "prompt_data": str(prompt_path)}
publish(root / "launch.json", launch)
- allowed = {"PATH", "HOME", "USER", "LANG", "LC_ALL", "TMPDIR", "LD_LIBRARY_PATH",
- "CUDA_VISIBLE_DEVICES", "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
- "http_proxy", "https_proxy", "no_proxy", "PYTHONPATH",
- "SLIME_DIR", "MEGATRON_DIR", "SGLANG_IMAGE"}
- child_env = {key: value for key, value in os.environ.items() if key in allowed}
- child_env["PATH"] = os.pathsep.join(
- [str(Path(sys.executable).parent), child_env.get("PATH", os.defpath)])
- child_env["PYTHONPATH"] = os.pathsep.join(filter(None, [
- child_env.get("SLIME_DIR", ""), child_env.get("MEGATRON_DIR", ""),
- str(ROOT), child_env.get("PYTHONPATH", ""),
- ]))
- child_env.update(environment)
- child = subprocess.Popen(plan["training_command"], cwd=ROOT, env=child_env, start_new_session=True)
-
- def forward_signal(signum, frame):
- if child.poll() is None:
- try:
- child.send_signal(signum)
- except ProcessLookupError:
- pass
-
- previous = {sig: signal.signal(sig, forward_signal) for sig in (signal.SIGINT, signal.SIGTERM)}
- try:
- return child.wait()
- finally:
- for sig, handler in previous.items():
- signal.signal(sig, handler)
+ target = local_target(plan, environment=environment)
+ publish(root / "effective-config.json", effective_config(plan, environment, target.env, source, checks))
+ target.launch()
+ return target.wait()
if __name__ == "__main__":
diff --git a/harborrl/platform/policy_pool.py b/harborrl/platform/policy_pool.py
index 697ebe2d4..945e9d09c 100644
--- a/harborrl/platform/policy_pool.py
+++ b/harborrl/platform/policy_pool.py
@@ -20,6 +20,8 @@ def validate_pool(states, expected=None):
def publish_pool(states, expected=None):
version = validate_pool(states, expected)
root = Path(os.environ['RUN_DIR'])
+ from harborrl.consistency.sglang import admit_pool
+ admit_pool(states, root)
payload = {'weight_version': version, 'engines': states, 'timestamp': time.time()}
temp = root / 'policy-pool.tmp'
temp.write_text(json.dumps(payload) + '\n')
diff --git a/harborrl/platform/preflight.py b/harborrl/platform/preflight.py
new file mode 100644
index 000000000..3a35fe88f
--- /dev/null
+++ b/harborrl/platform/preflight.py
@@ -0,0 +1,220 @@
+"""Live deployment checks for the native pipeline; no training dispatch.
+
+``doctor`` composes two layers: the deployment topology graph (manager, GPU
+target, rollout workers, control-loop addresses) and the machine-local
+environment checks. The local layer only runs where it is meaningful: when
+the GPU target is remote, dependency probes belong to the GPU job startup,
+not to this machine.
+"""
+import importlib
+import importlib.metadata
+import json
+import os
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+from types import SimpleNamespace
+
+from harborrl.config import ROOT
+
+
+def _deployment(plan):
+ return (plan.get("deployment") or {}).get("config")
+
+
+def doctor(plan):
+ config = plan["config"]
+ checks = []
+ deployment = _deployment(plan)
+ # Embedded-local is a compatibility record, not an opt-in: only an
+ # explicit deployment file activates the topology layers.
+ explicit = deployment is not None and plan.get("deployment", {}).get(
+ "source", "embedded-local") != "embedded-local"
+ local_gpu = deployment is None or deployment["gpu_target"]["transport"] == "local"
+ legacy_workers = (not explicit) or deployment["rollout"]["provider"] in ("embedded", "sandbox")
+
+ if explicit:
+ from harborrl.deployment.preflight import preflight as deployment_preflight
+ checks.extend(deployment_preflight(plan, deployment, live=True))
+ if not local_gpu:
+ checks.append({
+ "role": "gpu_target", "machine": deployment["gpu_target"]["id"],
+ "check": "gpu_target.training_dependencies", "ok": True,
+ "detail": "Slime/Megatron/SGlang versions are enforced fail-closed "
+ "by slime_train.sh at GPU job startup on the GPU machine",
+ })
+
+ runner = Path(config["harbor"]["python"])
+ checks.append({"path": "harbor.python", "ok": runner.is_file()})
+ from harborrl.rollout.harbor_job.coordinator import runner_command
+ if legacy_workers and runner.is_file():
+ workers = ([None] if config['harbor'].get('runner_transport') == 'local'
+ else config['harbor']['workers'])
+ for worker in workers:
+ try:
+ probe = subprocess.run(
+ runner_command(str(runner), ROOT, worker) + ["--probe"],
+ capture_output=True, text=True, timeout=30, check=False,
+ )
+ except (subprocess.SubprocessError, OSError) as exc:
+ probe = SimpleNamespace(returncode=1, stdout="", stderr=str(exc))
+ try:
+ payload = json.loads(probe.stdout)
+ runtime_version = tuple(payload.get("python", "").split(".")[:2])
+ version_ok = payload.get("harbor_version") == config["harbor"]["version"]
+ runtime_ok = len(runtime_version) == 2 and all(part.isdigit() for part in runtime_version)
+ except (ValueError, AttributeError, TypeError):
+ runtime_version, version_ok, runtime_ok = (), False, False
+ checks.append({
+ "service": "local Harbor runner" if worker is None else "external Harbor worker",
+ "worker": worker,
+ "ok": probe.returncode == 0 and runtime_ok
+ and tuple(map(int, runtime_version)) >= (3, 12) and version_ok,
+ "detail": probe.stdout.strip() or probe.stderr.strip()[-1000:],
+ })
+ if 'HARBORRL_NATIVE_SANDBOX' in plan['environment']:
+ for worker in workers:
+ try:
+ probe = subprocess.run(
+ runner_command(str(runner), ROOT, worker,
+ module='harborrl.rollout.harbor_job.sandbox_probe') + ['--stdin'],
+ input=plan['environment']['HARBORRL_NATIVE_SANDBOX'], cwd=ROOT,
+ capture_output=True, text=True, timeout=60, check=False)
+ checks.append({'service': 'sandbox offline runtime', 'worker': worker,
+ 'ok': probe.returncode == 0, 'detail': probe.stdout.strip()})
+ except (subprocess.SubprocessError, OSError) as exc:
+ checks.append({'service': 'sandbox offline runtime', 'worker': worker,
+ 'ok': False, 'detail': type(exc).__name__})
+
+ for key, path in (("tasks.catalog", config["tasks"]["catalog"]),
+ ("harness.profile", config["harness"]["profile"]),
+ ("model.checkpoint", config["model"]["checkpoint"]),
+ ("model.reference", config["model"]["reference"])):
+ checks.append({"path": key, "ok": Path(path).exists()})
+
+ model_config = Path(config["model"]["checkpoint"]) / "config.json"
+ if model_config.is_file():
+ try:
+ model = json.loads(model_config.read_text())
+ heads = model.get("num_attention_heads", 0)
+ kv_heads = model.get("num_key_value_heads", heads)
+ for tp in {config["deployment"]["actor_tensor_parallel_size"],
+ config["deployment"]["rollout_gpus_per_engine"]}:
+ checks.append({"model_tp": tp, "ok": bool(
+ heads and kv_heads and heads % tp == 0 and (kv_heads % tp == 0 or tp % kv_heads == 0))})
+ except (OSError, ValueError, TypeError) as exc:
+ checks.append({"path": "model.config", "ok": False, "detail": str(exc)})
+
+ from harborrl.tasks.catalog import catalog
+ try:
+ entries = catalog(config)
+ for entry in entries:
+ # catalog() already checks the complete task tree against its lock.
+ checks.append({"task": entry["id"], "ok": True, "detail": []})
+ except (ValueError, OSError, TypeError) as exc:
+ checks.append({"service": "native catalog", "ok": False, "detail": str(exc)})
+
+ if local_gpu:
+ for package in ("ray", "torch", "sglang", "transformers"):
+ try:
+ version = importlib.metadata.version(package)
+ except importlib.metadata.PackageNotFoundError:
+ version = None
+ checks.append({"dependency": package, "version": version, "ok": version is not None})
+
+ from harborrl.backends.slime_v032.versions import (
+ MEGATRON_COMMIT,
+ SGLANG_IMAGE_TAG,
+ SLIME_COMMIT,
+ )
+ if local_gpu:
+ slime_dir = os.environ.get("SLIME_DIR", "").strip()
+ slime_path = Path(slime_dir) if slime_dir else None
+ checks.append({"path": "SLIME_DIR", "ok": bool(slime_path and slime_path.is_dir()),
+ "detail": slime_dir or "not set"})
+ if slime_path and slime_path.is_dir():
+ checks.append({"path": "slime entrypoint", "ok": (slime_path / "train.py").is_file()})
+ preset = slime_path / "scripts" / "models" / f"{config['model']['args_file']}.sh"
+ checks.append({"path": "slime model preset", "ok": preset.is_file(), "detail": str(preset)})
+ try:
+ commit = subprocess.run(["git", "-C", str(slime_path), "rev-parse", "HEAD"],
+ capture_output=True, text=True, timeout=15, check=True).stdout.strip()
+ except (subprocess.SubprocessError, OSError) as exc:
+ commit = ""
+ checks.append({"service": "slime commit", "ok": False, "detail": str(exc)})
+ if commit:
+ checks.append({"service": "slime commit", "ok": commit == SLIME_COMMIT,
+ "detail": f"{commit} != {SLIME_COMMIT}" if commit != SLIME_COMMIT else commit})
+
+ megatron_dir = os.environ.get("MEGATRON_DIR", "").strip()
+ if megatron_dir:
+ try:
+ megatron_commit = subprocess.run(["git", "-C", megatron_dir, "rev-parse", "HEAD"],
+ capture_output=True, text=True, timeout=15, check=True).stdout.strip()
+ except (subprocess.SubprocessError, OSError) as exc:
+ megatron_commit = ""
+ checks.append({"service": "megatron commit", "ok": False, "detail": str(exc)})
+ if megatron_commit:
+ checks.append({"service": "megatron commit", "ok": megatron_commit == MEGATRON_COMMIT,
+ "detail": f"{megatron_commit} != {MEGATRON_COMMIT}" if megatron_commit != MEGATRON_COMMIT else megatron_commit})
+ else:
+ checks.append({"service": "megatron commit",
+ "ok": True,
+ "detail": "MEGATRON_DIR not set; official Slime image bundles Megatron"})
+
+ runtime_env = os.environ.copy()
+ runtime_pythonpath = os.pathsep.join(filter(None, [
+ str(slime_path), megatron_dir, str(ROOT), runtime_env.get("PYTHONPATH", ""),
+ ]))
+ runtime_env["PYTHONPATH"] = runtime_pythonpath
+ for module in ("slime.rollout.base_types", "slime.utils.types",
+ "slime.backends.megatron_utils.loss"):
+ try:
+ probe = subprocess.run([sys.executable, "-c", f"import {module}"],
+ capture_output=True, text=True, timeout=90, check=False,
+ env=runtime_env)
+ except (subprocess.SubprocessError, OSError) as exc:
+ probe = SimpleNamespace(returncode=1, stdout="", stderr=str(exc))
+ checks.append({"dependency": module, "ok": probe.returncode == 0,
+ "detail": probe.stderr.strip()[-1000:]})
+
+ sglang_image = os.environ.get("SGLANG_IMAGE", "").strip()
+ if sglang_image:
+ checks.append({"service": "sglang image", "ok": sglang_image.endswith(SGLANG_IMAGE_TAG),
+ "detail": sglang_image})
+
+ if shutil.which("nvidia-smi"):
+ try:
+ result = subprocess.run(["nvidia-smi", "--query-gpu=index", "--format=csv,noheader"],
+ capture_output=True, text=True, timeout=10, check=True)
+ visible = os.environ.get("CUDA_VISIBLE_DEVICES")
+ count = (len([item for item in visible.split(",") if item.strip() and item.strip() != "-1"])
+ if visible is not None else len(result.stdout.splitlines()))
+ checks.append({"resource": "visible GPU budget", "available": count,
+ "ok": count >= config["deployment"]["num_gpus"]})
+ except (subprocess.SubprocessError, OSError) as exc:
+ checks.append({"resource": "visible GPU budget", "ok": False, "detail": str(exc)})
+ else:
+ checks.append({"resource": "visible GPU budget", "ok": False, "detail": "nvidia-smi unavailable"})
+
+ if config["training"]["backend_contract"] != "slime-legacy":
+ from harborrl.backends.slime_v032.launcher import hook_modules
+ for hook_name, module_name in hook_modules().items():
+ try:
+ importlib.import_module(module_name)
+ checks.append({"service": "training backend hook", "hook": hook_name, "ok": True})
+ except (ImportError, AttributeError, TypeError, ValueError) as exc:
+ checks.append({
+ "service": "training backend hook", "hook": hook_name,
+ "ok": False, "detail": str(exc),
+ })
+
+ checks.append({
+ "service": "raw-model logprob audit",
+ "ok": True,
+ "detail": ("enabled by lock; live serving semantic probe remains a GPU gate"
+ if config["gateway"]["audited_raw_logprobs"] else
+ "disabled; generated evidence remains EVAL_ONLY and training will fail closed"),
+ })
+ return checks
diff --git a/harborrl/platform/slime_train.sh b/harborrl/platform/slime_train.sh
index 50377dbab..8d7972874 100644
--- a/harborrl/platform/slime_train.sh
+++ b/harborrl/platform/slime_train.sh
@@ -171,12 +171,19 @@ TRAIN_ARGS=(
export CUDA_DEVICE_MAX_CONNECTIONS=1
TRAIN_PYTHON="${TRAIN_PYTHON:-python3}"
export PYTHONPATH="${REPO_ROOT}${PYTHONPATH:+:${PYTHONPATH}}"
+ENTRY=("${SLIME_DIR}/train.py")
+if [[ "${HARBORRL_CONSISTENCY_MODE:-off}" != "off" ]]; then
+ ENTRY=(-m harborrl.consistency.train)
+fi
+
+"${TRAIN_PYTHON}" -m harborrl.platform.evidence "${RUN_DIR}" "${DRY_RUN:-0}" \
+ "${TRAIN_PYTHON}" -u "${ENTRY[@]}" "${TRAIN_ARGS[@]}"
if [[ "${DRY_RUN:-0}" == "1" ]]; then
printf '[dry-run] '
- printf '%q ' "${TRAIN_PYTHON}" -u "${SLIME_DIR}/train.py" "${TRAIN_ARGS[@]}"
+ printf '%q ' "${TRAIN_PYTHON}" -u "${ENTRY[@]}" "${TRAIN_ARGS[@]}"
printf '\n'
exit 0
fi
-exec "${TRAIN_PYTHON}" -u "${SLIME_DIR}/train.py" "${TRAIN_ARGS[@]}"
+exec "${TRAIN_PYTHON}" -u "${ENTRY[@]}" "${TRAIN_ARGS[@]}"
diff --git a/harborrl/rollout/exporters/native.py b/harborrl/rollout/exporters/native.py
index 55355c7aa..05fb63be8 100644
--- a/harborrl/rollout/exporters/native.py
+++ b/harborrl/rollout/exporters/native.py
@@ -1,47 +1,36 @@
-"""Trajectory-aware GRPO export, independent of Slime/Torch imports.
+"""Compatibility record view of the canonical trajectory-weighted GRPO batch.
-These records must enter a backend that honors token_weights and advantages.
-They must NOT be passed through the interactive reward/whitening builder.
+No independent advantage calculation or secondary whitening occurs here.
"""
-import math
-
-from harborrl.trajectories.native import Identity, finite, require_ready
+from harborrl.export.native import export_training_batch
+from harborrl.export.validate import token_weight
+from harborrl.trajectories.native import Identity
def export_group(trajectories, group_size, *, epsilon=1e-6, allow_synthetic=False):
if type(group_size) is not int or group_size < 2 or len(trajectories) != group_size:
raise ValueError("a complete group of at least two trajectories is required")
- if not finite(epsilon) or epsilon <= 0:
- raise ValueError("epsilon must be positive and finite")
- for ir in trajectories:
- require_ready(ir, allow_synthetic=allow_synthetic)
- ids = [Identity(**ir["identity"]) for ir in trajectories]
- if len({i.group_key for i in ids}) != 1:
- raise ValueError("mixed group, policy, task or profiles")
- for key in ("slot_id", "attempt_id", "trajectory_id"):
- if len({getattr(i, key) for i in ids}) != group_size:
- raise ValueError(f"duplicate {key}")
- rewards = [ir["evaluation"]["training_reward"] for ir in trajectories]
- mean = math.fsum(rewards) / group_size
- std = math.sqrt(math.fsum((r - mean) ** 2 for r in rewards) / group_size)
- if not finite(mean) or not finite(std):
- raise ValueError("nonfinite group statistics")
- advantages = [0.0 if std == 0 else (r - mean) / (std + epsilon) for r in rewards]
+ batch = export_training_batch(
+ [trajectories], group_size=group_size, epsilon=epsilon,
+ allow_synthetic=allow_synthetic,
+ )
+ group = batch.groups[0]
records = []
- for ir, ident, advantage in zip(trajectories, ids, advantages):
- count = sum(len(t["output_ids"]) for t in ir["turns"])
- for index, turn in enumerate(ir["turns"]):
- n = len(turn["output_ids"])
- records.append({"identity": ident.to_dict(), "turn_index": index,
- "tokens": turn["input_ids"] + turn["output_ids"],
- "prompt_length": len(turn["input_ids"]), "response_length": n,
- "loss_mask": [0] * len(turn["input_ids"]) + [1] * n,
- "old_logprobs": turn["logprobs"], "advantages": [advantage] * n,
- "token_weights": [1.0 / (group_size * count)] * n,
- "training_reward": ir["evaluation"]["training_reward"],
- "padding": False})
- return {"export_version": "native-grpo-v2", "group_key": ids[0].group_key,
- "trajectory_count": group_size, "mean": mean, "std": std,
- "advantages": advantages, "records": records,
+ for ir, unit in zip(trajectories, group.trajectories):
+ ident = Identity(**ir["identity"])
+ weight = token_weight(group_size, unit.token_count)
+ for span in unit.turns:
+ n = span.response_length
+ records.append({
+ "identity": ident.to_dict(), "turn_index": span.turn_index,
+ "tokens": list(span.tokens), "prompt_length": span.prompt_length,
+ "response_length": n, "loss_mask": [int(flag) for flag in span.loss_mask],
+ "old_logprobs": list(span.old_logprobs), "advantages": [unit.advantage] * n,
+ "token_weights": [weight] * n, "training_reward": ir["evaluation"]["training_reward"],
+ "padding": False,
+ })
+ return {"export_version": "native-grpo-v2", "group_key": group.group_key,
+ "trajectory_count": group_size, "mean": group.reward_mean, "std": group.reward_std,
+ "advantages": [unit.advantage for unit in group.trajectories], "records": records,
"synthetic": allow_synthetic,
"reduction": "sum_token_weighted_loss; no secondary whitening"}
diff --git a/harborrl/rollout/harbor_job/__main__.py b/harborrl/rollout/harbor_job/__main__.py
index 01828fd19..da10c1bb6 100644
--- a/harborrl/rollout/harbor_job/__main__.py
+++ b/harborrl/rollout/harbor_job/__main__.py
@@ -13,8 +13,10 @@ def main(argv=None):
smoke.add_argument("--output", required=True)
args = parser.parse_args(argv)
if args.command == "inspect":
- from harborrl.data.harbor.native_inspector import inspect_native
- result = inspect_native(args.task, expected_digest=args.expected_digest)
+ from harborrl.math_registry import NATIVE_TASK_PROVIDER
+ result = NATIVE_TASK_PROVIDER.adapter.inspect(
+ args.task, expected_digest=args.expected_digest
+ )
print(json.dumps(result, indent=2))
return int(result["status"] in ("INVALID", "UNSUPPORTED"))
from .offline import smoke as run_smoke
diff --git a/harborrl/rollout/harbor_job/bindings.py b/harborrl/rollout/harbor_job/bindings.py
index 4cd439689..e0e553e85 100644
--- a/harborrl/rollout/harbor_job/bindings.py
+++ b/harborrl/rollout/harbor_job/bindings.py
@@ -5,8 +5,9 @@
def claude_config(profile, gateway_url, credential, max_output_tokens=8192):
required = {"cli_version", "model", "max_turns", "agent_timeout_sec", "setup_timeout_sec", "verifier_timeout_sec"}
- if set(profile) != required:
- raise ValueError(f"Claude profile requires exactly {sorted(required)}")
+ allowed = required | {"disallowed_tools"}
+ if not required.issubset(profile) or set(profile) - allowed:
+ raise ValueError(f"Claude profile requires {sorted(required)} and optionally disallowed_tools")
if not isinstance(profile["cli_version"], str) or not re.fullmatch(r"\d+\.\d+\.\d+(?:-[A-Za-z0-9.]+)?", profile["cli_version"]):
raise ValueError("exact Claude CLI version required; latest is forbidden")
if not isinstance(profile["model"], str) or not re.fullmatch(r"[A-Za-z0-9_.:/-]+", profile["model"]):
@@ -14,6 +15,10 @@ def claude_config(profile, gateway_url, credential, max_output_tokens=8192):
for key in required - {"model", "cli_version"}:
if type(profile[key]) is not int or profile[key] <= 0:
raise ValueError(f"positive integer required: {key}")
+ disallowed_tools = profile.get("disallowed_tools", "Agent,Task")
+ if not isinstance(disallowed_tools, str) or not re.fullmatch(
+ r"[A-Za-z][A-Za-z0-9_]*(?:,[A-Za-z][A-Za-z0-9_]*)*", disallowed_tools):
+ raise ValueError("disallowed tools must be a comma-separated identifier list")
url = urlsplit(gateway_url)
if url.scheme not in ("http", "https") or not url.hostname or url.username or url.password or url.query or url.fragment or url.path not in ("", "/"):
raise ValueError("Anthropic base URL must be a plain origin (no /v1 suffix)")
@@ -27,9 +32,10 @@ def claude_config(profile, gateway_url, credential, max_output_tokens=8192):
"override_timeout_sec": profile["agent_timeout_sec"],
"override_setup_timeout_sec": profile["setup_timeout_sec"],
"kwargs": {"version": profile["cli_version"], "max_turns": profile["max_turns"],
- "max_thinking_tokens": 0, "disallowed_tools": "Agent,Task",
+ "max_thinking_tokens": 0, "disallowed_tools": disallowed_tools,
"permission_mode": "bypassPermissions"},
"env": {**client_controls(), "ANTHROPIC_API_KEY": credential,
+ "ANTHROPIC_MODEL": profile["model"],
"ANTHROPIC_BASE_URL": gateway_url.rstrip("/"),
"NO_PROXY": url.hostname, "no_proxy": url.hostname,
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": str(max_output_tokens)}}
diff --git a/harborrl/rollout/harbor_job/coordinator.py b/harborrl/rollout/harbor_job/coordinator.py
index 4afa6a27d..17047cd40 100644
--- a/harborrl/rollout/harbor_job/coordinator.py
+++ b/harborrl/rollout/harbor_job/coordinator.py
@@ -12,10 +12,9 @@
from dataclasses import replace
from pathlib import Path
+from harborrl.math_registry import CLAUDE_CODE_HARNESS
from harborrl.trajectories.native import assemble, publish
-from .audit import audit_session
-from .bindings import sanitized_runner_env
from .collector import collect
@@ -66,8 +65,7 @@ def export(self):
return export_group([self.completed[slot] for slot in self.identities], len(self.identities))
-def runner_command(runner_python, project, runner_host=None):
- module = "harborrl.rollout.harbor_job.runner"
+def runner_command(runner_python, project, runner_host=None, *, module="harborrl.rollout.harbor_job.runner"):
if runner_host is None:
return [runner_python, "-m", module]
remote = f"cd {shlex.quote(str(project))} && exec {shlex.quote(runner_python)} -m {module}"
@@ -77,13 +75,13 @@ def runner_command(runner_python, project, runner_host=None):
async def run_attempt(identity, *, task_path, profile, gateway_url, runner_python,
root, registry, reward_profile, timeout=1800, cleanup_timeout=120,
- max_output_tokens=8192, runner_host=None):
+ max_output_tokens=8192, runner_host=None, sandbox=None):
"""One real trial. CPU-only tests use a separate fake worker, never this path."""
root = Path(root).resolve()
root.mkdir(parents=True, exist_ok=False)
credential = secrets.token_urlsafe(32)
project = str(Path(__file__).resolve().parents[3])
- env = sanitized_runner_env(os.environ)
+ env = CLAUDE_CODE_HARNESS.adapter.runner_environment(os.environ)
env["PYTHONPATH"] = project
stderr = (root / "runner.stderr").open("wb")
try:
@@ -100,6 +98,12 @@ async def run_attempt(identity, *, task_path, profile, gateway_url, runner_pytho
req = {"identity": identity.to_dict(), "task_path": str(Path(task_path).resolve()), "profile": profile,
"gateway_url": gateway_url, "credential": credential, "output": str(root / "runner"),
"max_output_tokens": max_output_tokens}
+ if sandbox is not None:
+ from harborrl.trajectories.native import digest
+ req.update(sandbox=sandbox, runtime_digest=digest(sandbox))
+ timeout = (sandbox['create_timeout_s'] + sandbox['cleanup_timeout_s'] * 2
+ + sum(profile[k] for k in ('setup_timeout_sec', 'agent_timeout_sec', 'verifier_timeout_sec')))
+ cleanup_timeout = sandbox['cleanup_timeout_s'] + 15
process.stdin.write(json.dumps(req).encode() + b"\n")
await process.stdin.drain()
@@ -133,16 +137,26 @@ async def lifecycle():
await asyncio.wait_for(process.wait(), cleanup_timeout)
if process.returncode != 0 or terminal["type"] != "terminal":
raise RuntimeError("native trial did not finish normally")
+ if sandbox is not None:
+ from harborrl.trajectories.native import read_json
+ lifecycle_path = root / 'runner' / 'sandbox-lifecycle.json'
+ lifecycle = read_json(lifecycle_path) if lifecycle_path.is_file() else {}
+ if lifecycle.get('state') != 'deleted' or lifecycle.get('runtime_digest') != req['runtime_digest']:
+ publish(root / 'cleanup-required.json', {'identity': identity.to_dict(), 'reason': 'Sandbox deletion unconfirmed'})
+ raise RuntimeError('sandbox deletion unconfirmed')
trial_dir = Path(terminal["trial_dir"]).resolve()
result_path = Path(terminal["result_path"]).resolve()
if not trial_dir.is_relative_to(root / "runner" / "harbor") or result_path != trial_dir / "result.json":
raise ValueError("Runner result path is outside this attempt")
- sessions = [p for p in (trial_dir / "agent" / "sessions" / "projects").rglob("*.jsonl")]
+ sessions = list((trial_dir / "agent" / "sessions" / "projects").rglob("*.jsonl"))
if len(sessions) != 1:
raise ValueError("expected exactly one native Claude session; auxiliary sessions require review")
- audit_session(sessions[0], registry, identity.attempt_id)
+ registry.wait_delivered(identity.attempt_id, 30.0)
+ audit = CLAUDE_CODE_HARNESS.adapter.audit_session(sessions[0], registry, identity.attempt_id)
+ receipt = collect(identity, trial_id, result_path, trial_dir / "verifier", reward_profile)
+ CLAUDE_CODE_HARNESS.adapter.audit_empty_final(
+ trial_dir / "agent" / "claude-code.txt", registry, identity.attempt_id, audit)
turns, seal = registry.seal(identity.attempt_id)
- receipt = collect(identity, trial_id, terminal["result_path"], trial_dir / "verifier", reward_profile)
publish(root / "evaluation.json", receipt)
ir = assemble(identity, trial_id, turns, seal, receipt)
publish(root / "trajectory.v2.json", ir)
@@ -152,6 +166,8 @@ async def lifecycle():
publish(root / "failure.json", {"identity": identity.to_dict(), "error_type": type(exc).__name__})
raise
finally:
+ if sandbox is not None and (root / 'runner' / 'cleanup-required.json').exists():
+ publish(root / 'cleanup-required.json', {'identity': identity.to_dict(), 'reason': 'Inspect runner/cleanup-required.json'})
if registered:
registry.close(identity.attempt_id)
if process.returncode is None:
diff --git a/harborrl/rollout/harbor_job/environments/__init__.py b/harborrl/rollout/harbor_job/environments/__init__.py
new file mode 100644
index 000000000..d73e6b751
--- /dev/null
+++ b/harborrl/rollout/harbor_job/environments/__init__.py
@@ -0,0 +1 @@
+"""Optional Harbor environment adapters loaded only by the isolated runner."""
diff --git a/harborrl/rollout/harbor_job/environments/opensandbox.py b/harborrl/rollout/harbor_job/environments/opensandbox.py
new file mode 100644
index 000000000..829f9dadc
--- /dev/null
+++ b/harborrl/rollout/harbor_job/environments/opensandbox.py
@@ -0,0 +1,189 @@
+"""Audited lifecycle around Harbor's OpenSandbox environment."""
+import asyncio
+import fcntl
+import json
+import os
+import tempfile
+import time
+from contextlib import asynccontextmanager
+from datetime import timedelta
+from pathlib import Path
+
+import httpx
+from harbor.environments.opensandbox import OpenSandboxEnvironment
+
+from harborrl.rollout.harbor_job.sandbox_config import read_credential
+from harborrl.trajectories.native import digest, publish
+
+
+@asynccontextmanager
+async def startup_slot(directory, count):
+ """Process-shared creation slots; kernel releases locks on runner death."""
+ directory = Path(directory)
+ directory.mkdir(parents=True, exist_ok=True)
+ held = None
+ try:
+ while held is None:
+ for index in range(count):
+ handle = (directory / f'{index}.lock').open('a')
+ try:
+ fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ held = handle
+ break
+ except BlockingIOError:
+ handle.close()
+ if held is None:
+ await asyncio.sleep(0.2)
+ yield
+ finally:
+ if held is not None:
+ held.close()
+
+
+class ManagedOpenSandboxEnvironment(OpenSandboxEnvironment):
+ @classmethod
+ def validate_config(cls, cfg):
+ if cfg['options'] or any(b['options'] for b in cfg['bindings']):
+ raise ValueError('standard OpenSandbox adapter does not accept platform options')
+
+ @classmethod
+ def agent_gateway(cls, cfg, gateway_url):
+ return gateway_url
+
+ @classmethod
+ async def network_check(cls, cfg):
+ return {'status': 'not_probed', 'reason': 'Use the explicit sandbox smoke test for allocation checks'}
+
+ async def validate_binding(self):
+ pass
+
+ def extensions(self):
+ return {}
+
+ def api_params(self):
+ return {}
+
+ def startup_transport(self, allocated):
+ from harborrl.rollout.harbor_job.sandbox_transport import (
+ SandboxStartupTransport,
+ )
+ return SandboxStartupTransport(self.cfg['domain'].rstrip('/') + '/v1', allocated)
+
+ def __init__(self, *args, sandbox_config, binding, evidence_dir, startup_dir, gateway_url=None, **kwargs):
+ self.validate_config(sandbox_config)
+ self.cfg, self.binding = sandbox_config, binding
+ self.evidence = Path(evidence_dir)
+ self.startup_dir = startup_dir
+ self.gateway_url = gateway_url
+ self._startup_transport = None
+ self.record = {'runtime_digest': digest(sandbox_config),
+ 'image': binding['image'], 'sandbox_id': None, 'state': 'pending', 'events': []}
+ super().__init__(*args, domain=self.cfg['domain'], api_key=read_credential(self.cfg),
+ protocol=self.cfg['protocol'], use_server_proxy=True,
+ ready_timeout_sec=self.cfg['create_timeout_s'],
+ sandbox_timeout_sec=self.cfg['lifecycle_minutes'] * 60,
+ request_timeout_sec=self.cfg['request_timeout_s'], **kwargs)
+
+ def record_state(self, state):
+ self.record['state'] = state
+ self.record['events'].append({'state': state, 'time': time.time()})
+ publish(self.evidence / 'sandbox-events' / f'{len(self.record["events"]):04d}.json', self.record)
+ with tempfile.NamedTemporaryFile(mode='w', dir=self.evidence, delete=False) as stream:
+ json.dump(self.record, stream)
+ stream.flush()
+ os.fsync(stream.fileno())
+ os.replace(stream.name, self.evidence / 'sandbox-lifecycle.json')
+
+ def cleanup_required(self, reason):
+ publish(self.evidence / 'cleanup-required.json', {
+ 'sandbox_id': self.record['sandbox_id'], 'image': self.binding['image'],
+ 'reason': reason, 'runtime_digest': self.record['runtime_digest']})
+
+ async def _create_sandbox(self, sdk):
+ await self.validate_binding()
+ async with startup_slot(self.startup_dir, self.cfg['startup_concurrency']):
+ self.record_state('creating')
+ def allocated(sandbox_id):
+ self.record['sandbox_id'] = sandbox_id
+ self.record_state('allocated')
+
+ self._startup_transport = self.startup_transport(allocated)
+ connection = self._build_connection_config(sdk).model_copy(update={'transport': self._startup_transport})
+ try:
+ sandbox = await asyncio.wait_for(sdk['Sandbox'].create(
+ self.binding['image'], connection_config=connection,
+ timeout=timedelta(minutes=self.cfg['lifecycle_minutes']),
+ ready_timeout=timedelta(seconds=self.cfg['create_timeout_s']),
+ extensions=self.extensions(),
+ resource=self._build_resource(), env=dict(self._persistent_env),
+ network_policy=self._build_network_policy(sdk), skip_health_check=True),
+ self.cfg['create_timeout_s'])
+ except BaseException: # noqa: BLE001 - persist unknown allocation on cancellation too
+ if self.record['sandbox_id']:
+ await self._cleanup_allocated()
+ else:
+ self.record_state('creation_unknown')
+ self.cleanup_required('Creation outcome unknown; reconcile platform inventory before retry')
+ await self._startup_transport.aclose()
+ raise RuntimeError('Sandbox creation outcome unknown') from None
+ self._sandbox = sandbox
+ self.record['sandbox_id'] = sandbox.id
+ self.record_state('created')
+ try:
+ await asyncio.wait_for(self._wait_until_ready(sandbox), self.cfg['create_timeout_s'])
+ self.record_state('ready')
+ return sandbox
+ except BaseException: # noqa: BLE001 - cleanup before redacting SDK errors
+ await self.stop(delete=True)
+ raise RuntimeError('Sandbox readiness failed') from None
+
+ async def _cleanup_allocated(self):
+ """The SDK may already have killed an instance whose endpoints failed."""
+ self.record_state('deleting')
+ url = self.cfg['domain'].rstrip('/') + '/v1/sandboxes/' + self.record['sandbox_id']
+ try:
+ async with asyncio.timeout(self.cfg['cleanup_timeout_s']):
+ async with httpx.AsyncClient(timeout=30, headers={'OPEN-SANDBOX-API-KEY': self._api_key},
+ params=self.api_params()) as client:
+ status = await client.get(url)
+ if status.status_code != 404:
+ (await client.delete(url)).raise_for_status()
+ status = await client.get(url)
+ if status.status_code != 404:
+ raise RuntimeError('allocation deletion unconfirmed')
+ self.record_state('deleted')
+ except BaseException: # noqa: BLE001 - retain debt if cleanup is interrupted
+ self.record_state('delete_failed')
+ self.cleanup_required('Allocated Sandbox deletion was not confirmed')
+
+ async def start(self, force_build):
+ try:
+ await super().start(force_build)
+ except BaseException:
+ if self._sandbox is not None:
+ await self.stop(delete=True)
+ raise
+
+ async def stop(self, delete=True):
+ if self._sandbox is None:
+ if self._startup_transport is not None:
+ await self._startup_transport.aclose()
+ return
+ sandbox = self._sandbox
+ self.record_state('deleting')
+ try:
+ await asyncio.wait_for(sandbox.kill(), self.cfg['cleanup_timeout_s'])
+ self.record_state('deleted')
+ (self.evidence / 'cleanup-required.json').unlink(missing_ok=True)
+ self._sandbox = None
+ except BaseException: # noqa: BLE001 - preserve cleanup debt on cancellation
+ self.record_state('delete_failed')
+ self.cleanup_required('Sandbox kill was not acknowledged')
+ raise RuntimeError('Sandbox deletion failed') from None
+ finally:
+ try:
+ await asyncio.wait_for(sandbox.close(), 10)
+ except Exception: # noqa: BLE001 - close errors must not expose SAT headers
+ self.logger.warning('Sandbox SDK connection close failed')
+ if self._startup_transport is not None:
+ await self._startup_transport.aclose()
diff --git a/harborrl/rollout/harbor_job/offline.py b/harborrl/rollout/harbor_job/offline.py
index 7dfa3220e..8f5dea362 100644
--- a/harborrl/rollout/harbor_job/offline.py
+++ b/harborrl/rollout/harbor_job/offline.py
@@ -7,6 +7,7 @@
from harborrl.gateway.server import Gateway, make_server
from harborrl.gateway.trace import TraceRegistry
+from harborrl.math_registry import CLAUDE_CODE_HARNESS
from harborrl.rollout.exporters.native import export_group
from harborrl.trajectories.native import (
Identity,
@@ -17,7 +18,6 @@
require_ready,
)
-from .audit import audit_session
from .collector import collect
@@ -72,7 +72,9 @@ def smoke(output):
events.append({"sessionId": trial, "type": "assistant", "message": message})
session.write_text("".join(json.dumps(e) + "\n" for e in events))
registry.close(identity.attempt_id)
- audit_session(session, registry, identity.attempt_id)
+ CLAUDE_CODE_HARNESS.adapter.audit_session(
+ session, registry, identity.attempt_id
+ )
turns, seal = registry.seal(identity.attempt_id)
publish(directory / "result.json", {"id": trial, "finished_at": "synthetic", "verifier_environment_mode": "shared",
"verifier_result": {"rewards": {"reward": slot}}})
diff --git a/harborrl/rollout/harbor_job/runner.py b/harborrl/rollout/harbor_job/runner.py
index a5b1c9389..7aa23fe4a 100644
--- a/harborrl/rollout/harbor_job/runner.py
+++ b/harborrl/rollout/harbor_job/runner.py
@@ -14,11 +14,9 @@
import sys
from pathlib import Path
-from harborrl.data.harbor.native_inspector import inspect_native
+from harborrl.math_registry import CLAUDE_CODE_HARNESS, NATIVE_TASK_PROVIDER
from harborrl.trajectories.native import Identity, digest, publish
-from .bindings import claude_config
-
def probe():
if sys.version_info < (3, 12):
@@ -43,7 +41,9 @@ async def execute(request, emit, read_command):
identity = Identity(**request["identity"])
if digest(request["profile"]) != identity.harness_digest:
raise ValueError("harness profile differs from attempt manifest")
- inspection = inspect_native(request["task_path"], expected_digest=identity.task_digest)
+ inspection = NATIVE_TASK_PROVIDER.adapter.inspect(
+ request["task_path"], expected_digest=identity.task_digest
+ )
if inspection["status"] != "NEEDS_PROBE":
raise ValueError("native task preflight failed: " + ",".join(inspection["reasons"]))
root = Path(request["output"]).resolve()
@@ -51,12 +51,36 @@ async def execute(request, emit, read_command):
root.mkdir(parents=True, exist_ok=False)
publish(root / "manifest.json", {"identity": identity.to_dict(), "task": inspection,
"profile": request["profile"], "gateway_url": request["gateway_url"]})
+ sandbox = request.get('sandbox')
+ agent_gateway = request['gateway_url']
+ if sandbox is not None:
+ from harborrl.rollout.harbor_job.sandbox_config import environment_class
+ agent_gateway = environment_class(sandbox).agent_gateway(sandbox, agent_gateway)
+ agent = CLAUDE_CODE_HARNESS.adapter.agent_config(
+ request['profile'], agent_gateway, request['credential'], request['max_output_tokens'])
+ environment = {'type': 'docker', 'delete': True}
+ if sandbox is not None:
+ from harborrl.rollout.harbor_job.sandbox_config import task_binding
+ if digest(sandbox) != request.get('runtime_digest'):
+ raise ValueError('sandbox runtime lock mismatch')
+ if sandbox['installation'] and sandbox['installation']['version'] != request['profile']['cli_version']:
+ raise ValueError('offline harness version mismatch')
+ publish(root / 'runtime.json', {'config': sandbox, 'digest': digest(sandbox)})
+ environment = {'type': 'opensandbox', 'delete': True,
+ 'import_path': sandbox['adapter'],
+ 'kwargs': {'sandbox_config': sandbox, 'binding': task_binding(sandbox, request['task_path']),
+ 'evidence_dir': str(root),
+ 'gateway_url': request['gateway_url'],
+ 'startup_dir': str(root.parents[2] / '.sandbox-startup')}}
+ if sandbox['installation']:
+ agent.pop('name')
+ agent['import_path'] = 'harborrl.harnesses.offline_claude:OfflineClaudeCode'
+ agent['kwargs']['installation'] = sandbox['installation']
config = TrialConfig.model_validate({
"task": {"path": inspection["path"]},
"trial_name": digest(identity.to_dict()), "trials_dir": str(root / "harbor"),
- "agent": claude_config(request["profile"], request["gateway_url"], request["credential"],
- request["max_output_tokens"]),
- "environment": {"type": "docker", "delete": True},
+ "agent": agent,
+ "environment": environment,
"verifier": {"disable": False, "override_timeout_sec": request["profile"]["verifier_timeout_sec"]}})
trial = await Trial.create(config)
trial_id = str(trial.id)
diff --git a/harborrl/rollout/harbor_job/sandbox_config.py b/harborrl/rollout/harbor_job/sandbox_config.py
new file mode 100644
index 000000000..bb008e184
--- /dev/null
+++ b/harborrl/rollout/harbor_job/sandbox_config.py
@@ -0,0 +1,135 @@
+"""Non-secret Sandbox settings; no SDK imports in the training process."""
+import asyncio
+import copy
+import importlib
+import re
+from contextlib import asynccontextmanager
+from pathlib import Path
+from urllib.parse import urlsplit
+
+import yaml
+
+from harborrl.tasks.content import tomllib
+
+
+def _check_options(value):
+ if isinstance(value, dict):
+ for key, item in value.items():
+ if not isinstance(key, str):
+ raise TypeError('sandbox option names must be strings')
+ name = key.lower().replace('-', '_')
+ if name in {'token', 'credential', 'password', 'secret', 'authorization'} or name.endswith(('_api_key', '_token', '_secret')) or name == 'api_key':
+ raise ValueError('sandbox options must reference credential files, not contain secrets')
+ _check_options(item)
+ elif isinstance(value, list):
+ for item in value:
+ _check_options(item)
+
+
+def load_sandbox(path):
+ path = Path(path)
+ cfg = yaml.safe_load(path.read_text())
+ required = {'domain', 'credential_file', 'bindings'}
+ defaults = {'protocol': 'https', 'use_server_proxy': True,
+ 'request_timeout_s': 60, 'create_timeout_s': 600, 'lifecycle_minutes': 120,
+ 'cleanup_timeout_s': 120, 'max_live_instances': 2, 'startup_concurrency': 1,
+ 'adapter': 'harborrl.rollout.harbor_job.environments.opensandbox:ManagedOpenSandboxEnvironment',
+ 'options': {}, 'installation': None}
+ if not isinstance(cfg, dict) or not required <= set(cfg) or set(cfg) - required - defaults.keys():
+ raise ValueError('invalid sandbox configuration fields')
+ cfg = {**defaults, **copy.deepcopy(cfg)}
+ url = urlsplit(cfg['domain'])
+ if (url.scheme not in ('http', 'https')
+ or not url.hostname or url.username or url.password or url.query or url.fragment
+ or url.path.rstrip('/').endswith('/v1') or cfg['protocol'] != url.scheme):
+ raise ValueError('invalid sandbox domain/protocol')
+ for key in defaults.keys() - {'protocol', 'use_server_proxy', 'adapter', 'options', 'installation'}:
+ if type(cfg[key]) is not int or cfg[key] <= 0:
+ raise ValueError(f'sandbox {key} must be a positive integer')
+ if type(cfg['use_server_proxy']) is not bool or not isinstance(cfg['options'], dict):
+ raise ValueError('invalid sandbox proxy/options')
+ _check_options(cfg['options'])
+ if not isinstance(cfg['adapter'], str) or not re.fullmatch(r'[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*:[A-Za-z_]\w*', cfg['adapter']):
+ raise ValueError('sandbox adapter must be module:Class')
+ if not isinstance(cfg['credential_file'], str) or not cfg['credential_file']:
+ raise ValueError('sandbox credential_file required')
+ cfg['credential_file'] = str((path.parent / cfg['credential_file']).resolve())
+ bindings = cfg['bindings']
+ if not isinstance(bindings, list) or not bindings:
+ raise ValueError('sandbox image bindings required')
+ images = set()
+ for binding in bindings:
+ if not isinstance(binding, dict) or set(binding) - {'image', 'max_live_instances', 'options'} or not {'image', 'max_live_instances'} <= set(binding):
+ raise ValueError('invalid sandbox binding fields')
+ binding.setdefault('options', {})
+ _check_options(binding['options'])
+ if (not isinstance(binding['image'], str) or not binding['image']
+ or binding['image'] in images or not isinstance(binding['options'], dict)):
+ raise ValueError('invalid or duplicate sandbox image binding')
+ if type(binding['max_live_instances']) is not int or binding['max_live_instances'] <= 0:
+ raise ValueError('binding max_live_instances must be positive')
+ images.add(binding['image'])
+ install = cfg['installation']
+ if install is None:
+ return cfg
+ if not isinstance(install, dict) or set(install) != {'mode', 'version', 'binary', 'sha256'}:
+ raise ValueError('installation requires mode, version, binary, sha256')
+ if (install['mode'] != 'offline_binary' or not isinstance(install['version'], str)
+ or not re.fullmatch(r'\d+\.\d+\.\d+', install['version'])
+ or not isinstance(install['sha256'], str) or not re.fullmatch(r'[0-9a-f]{64}', install['sha256'])
+ or not isinstance(install['binary'], str) or not install['binary']):
+ raise ValueError('invalid pinned offline binary installation')
+ install['binary'] = str((path.parent / install['binary']).resolve())
+ return cfg
+
+
+def task_binding(cfg, task_path):
+ spec = tomllib.loads((Path(task_path) / 'task.toml').read_text())
+ image = spec.get('environment', {}).get('docker_image')
+ for binding in cfg['bindings']:
+ if image == binding['image']:
+ return binding
+ raise ValueError('sandbox task requires a prebuilt image with an exact binding')
+
+
+def environment_class(cfg):
+ """Import trusted deployment code only in the runner, never in GPU training."""
+ module, name = cfg['adapter'].split(':')
+ cls = getattr(importlib.import_module(module), name)
+ from harborrl.rollout.harbor_job.environments.opensandbox import (
+ ManagedOpenSandboxEnvironment,
+ )
+ if not isinstance(cls, type) or not issubclass(cls, ManagedOpenSandboxEnvironment):
+ raise TypeError('sandbox adapter must implement the audited OpenSandbox lifecycle')
+ cls.validate_config(cfg)
+ return cls
+
+
+def read_credential(cfg):
+ path = Path(cfg['credential_file'])
+ if path.stat().st_mode & 0o077:
+ raise ValueError('sandbox credential file must have mode 600 or stricter')
+ value = path.read_text().strip()
+ if not value or '\n' in value:
+ raise ValueError('sandbox credential file must contain one nonempty token')
+ return value
+
+
+class SandboxAdmission:
+ """One runtime owns capacity; unresolved cleanup blocks further admission."""
+ def __init__(self, cfg):
+ self.total = asyncio.Semaphore(cfg['max_live_instances'])
+ self.bindings = {b['image']: asyncio.Semaphore(b['max_live_instances']) for b in cfg['bindings']}
+ self.blocked = False
+
+ @asynccontextmanager
+ async def lease(self, binding, root):
+ # Wait for image-specific capacity before reserving a global live slot.
+ async with self.bindings[binding['image']], self.total:
+ if self.blocked:
+ raise RuntimeError('sandbox admission blocked by unresolved cleanup')
+ try:
+ yield
+ finally:
+ if (Path(root) / 'cleanup-required.json').exists():
+ self.blocked = True
diff --git a/harborrl/rollout/harbor_job/sandbox_probe.py b/harborrl/rollout/harbor_job/sandbox_probe.py
new file mode 100644
index 000000000..ebed3f47d
--- /dev/null
+++ b/harborrl/rollout/harbor_job/sandbox_probe.py
@@ -0,0 +1,63 @@
+"""Offline dependency check or explicit live Environment check (no allocation)."""
+import argparse
+import asyncio
+import hashlib
+import importlib.metadata
+import json
+import sys
+from pathlib import Path
+
+from harborrl.rollout.harbor_job.sandbox_config import (
+ environment_class,
+ load_sandbox,
+ read_credential,
+)
+
+
+def check(cfg):
+ from harborrl.rollout.harbor_job.runner import probe
+ result = probe()
+ version = importlib.metadata.version('opensandbox')
+ if version != '0.1.9':
+ raise ValueError('sandbox runner requires opensandbox==0.1.9')
+ from opensandbox.models.execd import RunCommandOpts
+
+ adapter = environment_class(cfg)
+ if not {'timeout', 'envs', 'working_directory'} <= RunCommandOpts.model_fields.keys():
+ raise ValueError('unsupported SDK command options')
+ checksum = None
+ if cfg['installation']:
+ with Path(cfg['installation']['binary']).open('rb') as source:
+ checksum = hashlib.file_digest(source, 'sha256').hexdigest()
+ if checksum != cfg['installation']['sha256']:
+ raise ValueError('offline binary checksum mismatch')
+ read_credential(cfg)
+ result.update(opensandbox_version=version, environment=adapter.__name__, binary_sha256=checksum)
+ return result
+
+
+async def network_check(cfg):
+ return await environment_class(cfg).network_check(cfg)
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('config', nargs='?')
+ parser.add_argument('--stdin', action='store_true')
+ parser.add_argument('--network', action='store_true')
+ args = parser.parse_args()
+ try:
+ cfg = json.load(sys.stdin) if args.stdin else load_sandbox(args.config)
+ result = check(cfg)
+ if args.network:
+ result['environments'] = asyncio.run(network_check(cfg))
+ print(json.dumps(result))
+ except Exception as exc: # noqa: BLE001 - redact authenticated SDK exceptions
+ # SDK/HTTP exceptions may contain authenticated endpoint URLs.
+ print(json.dumps({'ok': False, 'error_type': type(exc).__name__}))
+ return 1
+ return 0
+
+
+if __name__ == '__main__':
+ raise SystemExit(main())
diff --git a/harborrl/rollout/harbor_job/sandbox_transport.py b/harborrl/rollout/harbor_job/sandbox_transport.py
new file mode 100644
index 000000000..385ad5701
--- /dev/null
+++ b/harborrl/rollout/harbor_job/sandbox_transport.py
@@ -0,0 +1,27 @@
+"""Observe SDK allocation IDs without retrying creation requests."""
+import re
+
+import httpx
+
+
+class SandboxStartupTransport(httpx.AsyncBaseTransport):
+ def __init__(self, base_url, on_created, *, transport=None):
+ self.base_url = base_url.rstrip('/')
+ self.on_created = on_created
+ self.transport = transport or httpx.AsyncHTTPTransport()
+
+ async def handle_async_request(self, request):
+ url = str(request.url).split('?', 1)[0]
+ response = await self.transport.handle_async_request(request)
+ if request.method == 'POST' and url == self.base_url + '/sandboxes' and response.is_success:
+ await response.aread()
+ try:
+ sandbox_id = response.json()['id']
+ if isinstance(sandbox_id, str) and re.fullmatch(r'[A-Za-z0-9_-]+', sandbox_id):
+ self.on_created(sandbox_id)
+ except (ValueError, KeyError, TypeError):
+ pass # Let the SDK reject malformed creation responses.
+ return response
+
+ async def aclose(self):
+ await self.transport.aclose()
diff --git a/harborrl/rollout/native_generate.py b/harborrl/rollout/native_generate.py
index 5f59276e7..b5cfeebda 100644
--- a/harborrl/rollout/native_generate.py
+++ b/harborrl/rollout/native_generate.py
@@ -1,255 +1,17 @@
-"""Slime group hook for native Harbor trials.
-
-The hook keeps one trajectory per Slime slot and stores its immutable IR in
-metadata. Turn expansion happens only at the training converter boundary.
-"""
-from __future__ import annotations
-
-import asyncio
-import json
-import os
-import secrets
-from pathlib import Path
-
-from harborrl.rollout.harbor_job.coordinator import GroupSlots, run_attempt
-from harborrl.trajectories.native import Identity, RewardProfile, digest, read_json
-
-
-class NativeRolloutRuntime:
- def __init__(self, args):
- from slime.utils.processing_utils import load_tokenizer
-
- from harborrl.gateway.server import Gateway, make_server
- from harborrl.gateway.sglang_backend import SGLangBackend
- from harborrl.gateway.trace import TraceRegistry
-
- self.args = args
- self.run_root = Path(os.environ["RUN_DIR"]).resolve()
- self.catalog = read_json(os.environ["HARBORRL_NATIVE_CATALOG"])
- self.profile = read_json(os.environ["HARBORRL_NATIVE_PROFILE"])
- self.rollout_root = self.run_root / "native-rollouts"
- self.rollout_root.mkdir(parents=True, exist_ok=True)
- self.registry = TraceRegistry(self.run_root / "native-traces")
- engine_count = int(args.rollout_num_gpus) // int(args.rollout_num_gpus_per_engine)
- limit = max(1, int(args.sglang_server_concurrency) * engine_count)
- self.semaphore = asyncio.Semaphore(limit)
- tokenizer = load_tokenizer(args.hf_checkpoint, trust_remote_code=True)
- tokenizer_digest, template_digest = tokenizer_fingerprint(tokenizer)
- expected_tokenizer = os.environ["HARBORRL_NATIVE_TOKENIZER_DIGEST"]
- expected_template = os.environ["HARBORRL_NATIVE_TEMPLATE_DIGEST"]
- if (tokenizer_digest != expected_tokenizer or template_digest != expected_template):
- raise ValueError("live tokenizer/template digest differs from the native lock")
- endpoint = f"http://{args.sglang_router_ip}:{args.sglang_router_port}"
- backend = SGLangBackend(
- endpoint,
- tokenizer,
- tokenizer_digest=tokenizer_digest,
- template_digest=template_digest,
- max_context=int(args.rollout_max_context_len),
- audited_raw_logprobs=os.environ.get("HARBORRL_NATIVE_AUDITED_LOGPROBS") == "1",
- )
- self.backend = backend
- gateway = Gateway(
- self.registry,
- backend,
- model=self.profile["model"],
- max_output_tokens=int(args.rollout_max_response_len),
- )
- self.server = make_server(
- gateway,
- host=os.environ.get("HARBORRL_NATIVE_GATEWAY_HOST", "0.0.0.0"),
- port=int(os.environ["HARBORRL_NATIVE_GATEWAY_PORT"]),
- )
- self.thread = asyncio.get_running_loop().run_in_executor(None, self.server.serve_forever)
-
- def close(self):
- self.server.shutdown()
- self.server.server_close()
-
-
-def tokenizer_fingerprint(tokenizer):
- messages = [{"role": "user", "content": "HarborRL native tokenizer/template fingerprint"}]
- kwargs = {"tokenize": True, "add_generation_prompt": True, "enable_thinking": False}
- rendered = tokenizer.apply_chat_template(messages, **kwargs)
- template = digest({"messages": messages, "kwargs": kwargs, "token_ids": rendered})
- vocabulary = tokenizer.get_vocab()
- tokenizer_digest = digest({
- "class": type(tokenizer).__name__,
- "vocab": sorted((str(key), int(value)) for key, value in vocabulary.items()),
- })
- return tokenizer_digest, template
-
-
-_RUNTIME = None
-_RUNTIME_LOCK = asyncio.Lock()
-
-
-async def runtime(args):
- global _RUNTIME
- if _RUNTIME is None:
- async with _RUNTIME_LOCK:
- if _RUNTIME is None:
- _RUNTIME = NativeRolloutRuntime(args)
- return _RUNTIME
-
-
-def publish_native_version(states, expected):
- """Open the next admission epoch only after the complete pool is healthy."""
- if _RUNTIME is None:
- return
- _RUNTIME.registry.drain(float(os.environ.get("HARBORRL_NATIVE_DRAIN_TIMEOUT", "30")))
- _RUNTIME.registry.publish_version(states, str(expected))
-
-
-def current_policy_version(runtime_):
- """Publish and lock the version currently served by the rollout pool."""
- states = runtime_.backend.pool_states()
- from harborrl.platform.policy_pool import publish_pool
- version = publish_pool(states)
- runtime_.registry.drain(float(os.environ.get("HARBORRL_NATIVE_DRAIN_TIMEOUT", "30")))
- runtime_.registry.publish_version(states, version)
- return version
-
-
-def close_runtime():
- global _RUNTIME
- if _RUNTIME is not None:
- _RUNTIME.close()
- _RUNTIME = None
-
-
-def _task_entry(sample):
- metadata = sample.metadata if isinstance(sample.metadata, dict) else {}
- entry = metadata.get("native_task")
- required = {"id", "revision", "path", "task_digest", "reward_profile"}
- if not isinstance(entry, dict) or set(entry) != required:
- raise ValueError("native rollout requires a locked task catalog entry")
- return entry
-
-
-def _identity(
- args,
- sample,
- slot,
- rollout_id,
- task_digest,
- reward_digest,
- sampling_digest,
- policy_version,
-):
- group_index = getattr(sample, "group_index", None)
- if type(group_index) is not int:
- raise ValueError("Slime must provide group identity before native generation")
- return Identity(
- run_id=os.environ["RUN_ID"],
- batch_id=f"rollout-{rollout_id}",
- group_id=f"group-{group_index}",
- slot_id=str(slot),
- attempt_id=secrets.token_hex(16),
- trajectory_id=secrets.token_hex(16),
- task_digest=task_digest,
- policy_version=str(policy_version),
- harness_digest=digest(read_json(os.environ["HARBORRL_NATIVE_PROFILE"])),
- reward_digest=reward_digest,
- sampling_digest=sampling_digest,
- )
-
-
-async def _run_slot(
- args,
- runtime_,
- sample,
- slot,
- rollout_id,
- entry,
- sampling_digest,
- policy_version,
-):
- reward_profile = RewardProfile(**entry["reward_profile"])
- identities = [
- _identity(
- args,
- sample,
- index,
- rollout_id=rollout_id,
- task_digest=entry["task_digest"],
- reward_digest=reward_profile.digest,
- sampling_digest=sampling_digest,
- policy_version=policy_version,
- )
- for index in range(args.n_samples_per_prompt)
- ]
- slots = GroupSlots(identities, max_attempts=int(os.environ["HARBORRL_NATIVE_MAX_ATTEMPTS"]))
- workers = json.loads(os.environ["HARBORRL_NATIVE_RUNNER_WORKERS"])
- task_workers = json.loads(os.environ.get("HARBORRL_NATIVE_TASK_WORKER_OVERRIDES", "{}"))
- for attempt_index in range(slots.max_attempts):
- identity = slots.start(str(slot))
- runner_host = task_workers.get(entry["id"])
- if runner_host is None:
- runner_host = workers[(slot + attempt_index) % len(workers)]
- root = runtime_.rollout_root / identity.group_id / identity.attempt_id
- try:
- async with runtime_.semaphore:
- ir = await run_attempt(
- identity,
- task_path=entry["path"],
- profile=runtime_.profile,
- gateway_url=os.environ["HARBORRL_NATIVE_GATEWAY_URL"],
- runner_python=os.environ["HARBORRL_NATIVE_RUNNER_PYTHON"],
- runner_host=runner_host,
- root=root,
- registry=runtime_.registry,
- reward_profile=reward_profile,
- max_output_tokens=int(args.rollout_max_response_len),
- )
- except (OSError, ValueError, RuntimeError, asyncio.TimeoutError) as exc:
- slots.finish(str(slot), error=f"{type(exc).__name__}: {exc}")
- continue
- slots.finish(str(slot), ir)
- return ir
- raise RuntimeError(f"native slot {slot} exhausted its retry budget")
-
-
-async def generate_group(args, group, sampling_params, rollout_id, evaluation=False):
- """Run one complete native group; evaluation is intentionally unsupported."""
- from harborrl.data.harbor.native_inspector import inspect_native
-
- if evaluation:
- raise ValueError("native training rollout cannot be reused as evaluation")
- if len(group) != args.n_samples_per_prompt:
- raise ValueError("native rollout group is incomplete before launch")
- entries = [_task_entry(sample) for sample in group]
- if any(entry != entries[0] for entry in entries[1:]):
- raise ValueError("native rollout group mixes task locks")
- entry = entries[0]
- report = inspect_native(entry["path"], expected_digest=entry["task_digest"])
- if report["status"] != "NEEDS_PROBE":
- raise ValueError(f"native task preflight failed: {report['reasons']}")
- stop = list(sampling_params.get("stop") or [])
- sampling_digest = digest({
- "temperature": sampling_params.get("temperature"),
- "top_p": sampling_params.get("top_p"),
- "top_k": sampling_params.get("top_k"),
- "max_new_tokens": sampling_params.get("max_new_tokens"),
- "stop": stop,
- })
- runtime_ = await runtime(args)
- policy_version = current_policy_version(runtime_)
- trajectories = await asyncio.gather(*[
- _run_slot(
- args, runtime_, sample, slot, rollout_id, entry, sampling_digest, policy_version
- )
- for slot, sample in enumerate(group)
- ])
- output = []
- for sample, ir in zip(group, trajectories):
- sample.metadata["native_ir"] = ir
- sample.response = "native Harbor trial"
- sample.reward = {"score": ir["evaluation"]["training_reward"]}
- sample.status = sample.Status.COMPLETED
- try:
- sample.policy_version = int(ir["identity"]["policy_version"])
- except ValueError as exc:
- raise ValueError("native policy version must be an integer in Slime") from exc
- output.append(sample)
- return output
+"""Compatibility imports for the framework-independent rollout runtime."""
+from harborrl.rollout.runtime import ( # noqa: F401
+ GroupSlots,
+ NativeRolloutRuntime,
+ RolloutRuntime,
+ RolloutRuntimeConfig,
+ close_runtime,
+ current_policy_version,
+ publish_native_version,
+ run_attempt,
+ run_group,
+ run_slot,
+ runtime,
+ slot_identity,
+ task_entry,
+ tokenizer_fingerprint,
+)
diff --git a/harborrl/rollout/runtime.py b/harborrl/rollout/runtime.py
new file mode 100644
index 000000000..558bab01d
--- /dev/null
+++ b/harborrl/rollout/runtime.py
@@ -0,0 +1,350 @@
+"""Extracted rollout core (P2): gateway, traces and complete groups.
+
+This module owns rollout execution and must not import Slime or Ray: training
+frameworks live in ``harborrl.backends.*`` adapters that build a
+``RolloutRuntimeConfig`` and hand over a tokenizer. Group semantics, worker
+scheduling, retries and the RL_READY contract stay exactly as the embedded
+pipeline enforced them.
+"""
+from __future__ import annotations
+
+import asyncio
+import json
+import secrets
+import threading
+from contextlib import nullcontext
+from dataclasses import dataclass
+from pathlib import Path
+
+from harborrl.models.qwen import normalize_chat_template_ids
+from harborrl.rollout.harbor_job.coordinator import GroupSlots, run_attempt
+from harborrl.trajectories.native import Identity, RewardProfile, digest, read_json
+
+TASK_ENTRY_KEYS = {"id", "revision", "path", "task_digest", "reward_profile"}
+
+
+@dataclass(frozen=True)
+class RolloutRuntimeConfig:
+ """Deployment-independent description of one run's rollout machinery."""
+
+ run_id: str
+ run_root: str
+ catalog_path: str
+ profile_path: str
+ gateway_host: str
+ gateway_port: int
+ gateway_url: str
+ runner_python: str
+ workers: tuple
+ task_worker_overrides: dict
+ max_attempts: int
+ max_output_tokens: int
+ max_context: int
+ concurrency_limit: int
+ group_size: int
+ expected_tokenizer_digest: str
+ expected_template_digest: str
+ audited_raw_logprobs: bool
+ drain_timeout: float
+ model_tag: str
+ endpoint: str
+ sandbox: dict = None
+ runner_transport: str = 'ssh'
+
+ @staticmethod
+ def _required(env, name):
+ value = env.get(name)
+ if not isinstance(value, str) or not value.strip():
+ raise ValueError(f"rollout runtime requires {name}")
+ return value
+
+ @classmethod
+ def from_environment(cls, env, *, endpoint, concurrency_limit, group_size,
+ gateway_host=None, gateway_port=None, gateway_url=None,
+ workers=None, runner_python=None):
+ if not isinstance(endpoint, str) or not endpoint.strip():
+ raise ValueError("rollout runtime requires a serving endpoint")
+ if type(group_size) is not int or group_size < 2:
+ raise ValueError("rollout runtime requires a group size of at least two")
+ if workers is None:
+ workers = json.loads(cls._required(env, "HARBORRL_NATIVE_RUNNER_WORKERS"))
+ if runner_python is None:
+ runner_python = cls._required(env, "HARBORRL_NATIVE_RUNNER_PYTHON")
+ try:
+ overrides = json.loads(env.get("HARBORRL_NATIVE_TASK_WORKER_OVERRIDES") or "{}")
+ except ValueError as exc:
+ raise ValueError("invalid HARBORRL_NATIVE_TASK_WORKER_OVERRIDES") from exc
+ return cls(
+ run_id=cls._required(env, "RUN_ID"),
+ run_root=cls._required(env, "RUN_DIR"),
+ catalog_path=cls._required(env, "HARBORRL_NATIVE_CATALOG"),
+ profile_path=cls._required(env, "HARBORRL_NATIVE_PROFILE"),
+ gateway_host=gateway_host or env.get("HARBORRL_NATIVE_GATEWAY_HOST", "0.0.0.0"),
+ gateway_port=int(gateway_port or cls._required(env, "HARBORRL_NATIVE_GATEWAY_PORT")),
+ gateway_url=gateway_url or cls._required(env, "HARBORRL_NATIVE_GATEWAY_URL"),
+ runner_python=runner_python,
+ workers=tuple(workers),
+ task_worker_overrides=dict(overrides),
+ max_attempts=int(env.get("HARBORRL_NATIVE_MAX_ATTEMPTS", "2")),
+ max_output_tokens=int(env.get("ROLLOUT_MAX_RESPONSE_LEN", "8192")),
+ max_context=int(env.get("ROLLOUT_MAX_CONTEXT_LEN", "32768")),
+ concurrency_limit=max(1, int(concurrency_limit)),
+ group_size=int(group_size),
+ expected_tokenizer_digest=cls._required(env, "HARBORRL_NATIVE_TOKENIZER_DIGEST"),
+ expected_template_digest=cls._required(env, "HARBORRL_NATIVE_TEMPLATE_DIGEST"),
+ audited_raw_logprobs=env.get("HARBORRL_NATIVE_AUDITED_LOGPROBS") == "1",
+ drain_timeout=float(env.get("HARBORRL_NATIVE_DRAIN_TIMEOUT", "30")),
+ model_tag=env.get("MODEL_TAG", env.get("MODEL_ARGS_FILE", "qwen3-8B")),
+ endpoint=endpoint.rstrip("/"),
+ sandbox=json.loads(env.get('HARBORRL_NATIVE_SANDBOX', 'null')),
+ runner_transport=env.get('HARBORRL_NATIVE_RUNNER_TRANSPORT', 'ssh'),
+ )
+
+ def environment(self):
+ """Render the environment mapping this config was built from."""
+ return {
+ "RUN_ID": self.run_id, "RUN_DIR": self.run_root,
+ "HARBORRL_NATIVE_CATALOG": self.catalog_path,
+ "HARBORRL_NATIVE_PROFILE": self.profile_path,
+ "HARBORRL_NATIVE_GATEWAY_HOST": self.gateway_host,
+ "HARBORRL_NATIVE_GATEWAY_PORT": str(self.gateway_port),
+ "HARBORRL_NATIVE_GATEWAY_URL": self.gateway_url,
+ "HARBORRL_NATIVE_RUNNER_PYTHON": self.runner_python,
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(list(self.workers)),
+ "HARBORRL_NATIVE_TASK_WORKER_OVERRIDES": json.dumps(
+ self.task_worker_overrides, sort_keys=True),
+ "HARBORRL_NATIVE_MAX_ATTEMPTS": str(self.max_attempts),
+ "ROLLOUT_MAX_RESPONSE_LEN": str(self.max_output_tokens),
+ "ROLLOUT_MAX_CONTEXT_LEN": str(self.max_context),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": self.expected_tokenizer_digest,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": self.expected_template_digest,
+ "HARBORRL_NATIVE_AUDITED_LOGPROBS": str(int(self.audited_raw_logprobs)),
+ "HARBORRL_NATIVE_DRAIN_TIMEOUT": str(self.drain_timeout),
+ "MODEL_TAG": self.model_tag,
+ "HARBORRL_NATIVE_SANDBOX": json.dumps(self.sandbox),
+ "HARBORRL_NATIVE_RUNNER_TRANSPORT": self.runner_transport,
+ }
+
+
+def tokenizer_fingerprint(tokenizer):
+ messages = [{"role": "user", "content": "HarborRL native tokenizer/template fingerprint"}]
+ kwargs = {"tokenize": True, "add_generation_prompt": True, "enable_thinking": False}
+ rendered_ids = normalize_chat_template_ids(
+ tokenizer.apply_chat_template(messages, **kwargs))
+ vocabulary = tokenizer.get_vocab()
+ tokenizer_digest = digest({
+ "class": type(tokenizer).__name__,
+ "vocab": sorted((str(key), int(value)) for key, value in vocabulary.items()),
+ })
+ template = digest({"messages": messages, "kwargs": kwargs, "token_ids": rendered_ids})
+ return tokenizer_digest, template
+
+
+class RolloutRuntime:
+ """One gateway, one trace registry and one rollout root per run."""
+
+ def __init__(self, config, tokenizer, *, backend=None):
+ from harborrl.gateway.server import Gateway, make_server
+ from harborrl.gateway.sglang_backend import SGLangBackend
+ from harborrl.gateway.trace import TraceRegistry
+ from harborrl.math_registry import MATH_REGISTRY
+
+ self.config = config
+ self.run_root = Path(config.run_root).resolve()
+ self.catalog = read_json(config.catalog_path)
+ self.profile = read_json(config.profile_path)
+ self.rollout_root = self.run_root / "native-rollouts"
+ self.rollout_root.mkdir(parents=True, exist_ok=True)
+ self.registry = TraceRegistry(self.run_root / "native-traces")
+ self.semaphore = asyncio.Semaphore(config.concurrency_limit)
+ if config.sandbox:
+ from harborrl.rollout.harbor_job.sandbox_config import SandboxAdmission
+ self.sandbox_admission = SandboxAdmission(config.sandbox)
+ tokenizer_digest, template_digest = tokenizer_fingerprint(tokenizer)
+ if (tokenizer_digest != config.expected_tokenizer_digest
+ or template_digest != config.expected_template_digest):
+ raise ValueError("live tokenizer/template digest differs from the native lock")
+ model = MATH_REGISTRY.model_for_preset(config.model_tag)
+ self.backend = backend or SGLangBackend(
+ config.endpoint, tokenizer, model.adapter,
+ tokenizer_digest=tokenizer_digest, template_digest=template_digest,
+ max_context=config.max_context,
+ audited_raw_logprobs=config.audited_raw_logprobs,
+ )
+ gateway = Gateway(self.registry, self.backend, model=self.profile["model"],
+ max_output_tokens=config.max_output_tokens)
+ self.server = make_server(gateway, host=config.gateway_host,
+ port=config.gateway_port)
+ self._start_gateway()
+
+ def _start_gateway(self):
+ self.thread = threading.Thread(
+ target=self.server.serve_forever, name="harborrl-native-gateway", daemon=True
+ )
+ self.thread.start()
+
+ def close(self):
+ self.server.shutdown()
+ if getattr(self.thread, "is_alive", lambda: False)():
+ self.thread.join(5)
+ self.server.server_close()
+
+ def manifest_path(self, identity):
+ """Deterministic shared-storage path of one trajectory manifest."""
+ if isinstance(identity, dict):
+ identity = Identity(**identity)
+ return self.rollout_root / identity.group_id / identity.attempt_id / "trajectory.v2.json"
+
+
+# Historical name kept for existing hooks and evidence readers.
+NativeRolloutRuntime = RolloutRuntime
+
+_RUNTIME = None
+_RUNTIME_LOCK = asyncio.Lock()
+
+
+async def runtime(config, *, tokenizer=None, tokenizer_loader=None):
+ """Process-wide runtime cache for the embedded GPU-side rollout path."""
+ global _RUNTIME
+ if _RUNTIME is None:
+ async with _RUNTIME_LOCK:
+ if _RUNTIME is None:
+ if tokenizer is None:
+ if tokenizer_loader is None:
+ raise ValueError("runtime requires a tokenizer or a loader")
+ tokenizer = tokenizer_loader()
+ _RUNTIME = RolloutRuntime(config, tokenizer)
+ return _RUNTIME
+
+
+def publish_native_version(runtime_, states, expected):
+ """Open the next admission epoch only after the complete pool is healthy."""
+ runtime_.registry.drain(runtime_.config.drain_timeout)
+ runtime_.registry.publish_version(states, str(expected))
+
+
+def current_policy_version(runtime_):
+ """Publish and lock the version currently served by the rollout pool."""
+ states = runtime_.backend.pool_states()
+ from harborrl.platform.policy_pool import publish_pool
+ version = publish_pool(states)
+ runtime_.registry.drain(runtime_.config.drain_timeout)
+ runtime_.registry.publish_version(states, version)
+ return version
+
+
+def close_runtime():
+ global _RUNTIME
+ if _RUNTIME is not None:
+ _RUNTIME.close()
+ _RUNTIME = None
+
+
+def task_entry(sample):
+ metadata = sample.metadata if isinstance(sample.metadata, dict) else {}
+ entry = metadata.get("native_task")
+ if not isinstance(entry, dict) or set(entry) != TASK_ENTRY_KEYS:
+ raise ValueError("native rollout requires a locked task catalog entry")
+ return entry
+
+
+def slot_identity(config, group_index, slot, rollout_id, entry,
+ reward_digest, sampling_digest, policy_version, *, harness_digest=None):
+ return Identity(
+ run_id=config.run_id,
+ batch_id=f"rollout-{rollout_id}",
+ group_id=f"group-{group_index}",
+ slot_id=str(slot),
+ attempt_id=secrets.token_hex(16),
+ trajectory_id=secrets.token_hex(16),
+ task_digest=entry["task_digest"],
+ policy_version=str(policy_version),
+ harness_digest=harness_digest if harness_digest is not None else digest(read_json(config.profile_path)),
+ reward_digest=reward_digest,
+ sampling_digest=sampling_digest,
+ )
+
+
+def _group_slots(config, runtime_, group_index, rollout_id, entry,
+ sampling_digest, policy_version, size):
+ reward_profile = RewardProfile(**entry["reward_profile"])
+ harness_digest = digest(runtime_.profile)
+ identities = [
+ slot_identity(config, group_index, index, rollout_id, entry,
+ reward_profile.digest, sampling_digest, policy_version,
+ harness_digest=harness_digest)
+ for index in range(size)
+ ]
+ return GroupSlots(identities, max_attempts=config.max_attempts), reward_profile
+
+
+async def run_slot(config, runtime_, group_index, slot, rollout_id, entry,
+ sampling_digest, policy_version):
+ """Compatibility entry point for executing an individual group slot."""
+ slots, reward_profile = _group_slots(
+ config, runtime_, group_index, rollout_id, entry,
+ sampling_digest, policy_version, config.group_size)
+ return await _run_slot(config, runtime_, slots, slot, entry, reward_profile)
+
+
+async def _run_slot(config, runtime_, slots, slot, entry, reward_profile):
+ """Execute one slot with the group's shared identity and retry bookkeeping."""
+ workers = config.workers
+ overrides = config.task_worker_overrides
+ for attempt_index in range(slots.max_attempts):
+ identity = slots.start(str(slot))
+ runner_host = overrides.get(entry["id"])
+ if config.runner_transport == 'local':
+ runner_host = None
+ elif runner_host is None:
+ if not workers:
+ raise ValueError("rollout runtime requires at least one worker destination")
+ runner_host = workers[(slot + attempt_index) % len(workers)]
+ root = runtime_.rollout_root / identity.group_id / identity.attempt_id
+ admission = nullcontext()
+ if config.sandbox:
+ from harborrl.rollout.harbor_job.sandbox_config import task_binding
+ admission = runtime_.sandbox_admission.lease(task_binding(config.sandbox, entry['path']), root)
+ try:
+ async with admission, runtime_.semaphore:
+ ir = await run_attempt(
+ identity,
+ task_path=entry["path"],
+ profile=runtime_.profile,
+ gateway_url=config.gateway_url,
+ runner_python=config.runner_python,
+ runner_host=runner_host,
+ root=root,
+ registry=runtime_.registry,
+ reward_profile=reward_profile,
+ max_output_tokens=config.max_output_tokens,
+ **({'sandbox': config.sandbox} if config.sandbox else {}),
+ )
+ except (OSError, ValueError, RuntimeError, asyncio.TimeoutError) as exc:
+ slots.finish(str(slot), error=f"{type(exc).__name__}: {exc}")
+ if config.sandbox and ((root / 'cleanup-required.json').exists() or runtime_.sandbox_admission.blocked):
+ raise RuntimeError('sandbox cleanup unresolved; automatic retry blocked') from exc
+ continue
+ slots.finish(str(slot), ir)
+ return ir
+ raise RuntimeError(f"native slot {slot} exhausted its retry budget")
+
+
+async def run_group(config, runtime_, *, entry, group_index, rollout_id,
+ sampling_digest, policy_version, group_size=None):
+ """Execute one complete group and return per-slot IRs in slot order."""
+ size = config.group_size if group_size is None else int(group_size)
+ slots, reward_profile = _group_slots(
+ config, runtime_, group_index, rollout_id, entry,
+ sampling_digest, policy_version, size)
+ tasks = [asyncio.create_task(
+ _run_slot(config, runtime_, slots, slot, entry, reward_profile))
+ for slot in range(size)
+ ]
+ try:
+ return list(await asyncio.gather(*tasks))
+ except BaseException:
+ for task in tasks:
+ task.cancel()
+ await asyncio.gather(*tasks, return_exceptions=True)
+ raise
diff --git a/harborrl/rollout/service.py b/harborrl/rollout/service.py
new file mode 100644
index 000000000..e223ab05a
--- /dev/null
+++ b/harborrl/rollout/service.py
@@ -0,0 +1,562 @@
+"""RolloutService (P3): the manager-side HTTP control plane for rollout groups.
+
+First-phase contract: HTTP JSON, run-scoped tokens, idempotent group
+submission. Bulky payloads (trajectory manifests, token ids, logprobs, loss
+masks) live on the shared run directory; HTTP responses carry paths plus
+SHA-256 digests, and the SlimeAdapter recomputes every digest before
+converting anything into training samples.
+
+Rules that keep the lifecycle safe:
+- same group id and same content -> the existing state, never a second run;
+- same group id and different content -> rejected;
+- after a timeout the client must query, never resubmit;
+- a new policy version is admitted only after the whole serving pool is
+ healthy on it (drain first, publish second).
+"""
+from __future__ import annotations
+
+import asyncio
+import hashlib
+import hmac
+import json
+import secrets
+import threading
+import time
+from dataclasses import dataclass, field
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+from urllib.parse import urlsplit
+
+from harborrl.rollout.exporters.native import export_group
+from harborrl.rollout.runtime import run_group
+from harborrl.trajectories.native import digest, finite, publish, read_json
+
+SERVICE_SCHEMA = "harborrl-rollout-service-v1"
+GROUP_REQUEST_KEYS = {"group_id", "rollout_id", "group_index", "policy_version",
+ "task", "sampling", "slots"}
+SAMPLING_KEYS = {"temperature", "top_p", "top_k", "max_new_tokens", "stop"}
+TASK_ENTRY_KEYS = {"id", "revision", "path", "task_digest", "reward_profile"}
+GROUP_DIR = "rollout-service/groups"
+
+
+class ServiceError(Exception):
+ """Maps onto one HTTP status; carries a machine-readable code."""
+
+ def __init__(self, status, code, message):
+ super().__init__(message)
+ self.status, self.code = status, code
+
+
+def validate_group_request(request):
+ if not isinstance(request, dict) or set(request) != GROUP_REQUEST_KEYS:
+ raise ServiceError(400, "invalid_request",
+ f"group request requires exactly {sorted(GROUP_REQUEST_KEYS)}")
+ for key in ("group_id", "policy_version"):
+ if not isinstance(request[key], str) or not request[key].strip():
+ raise ServiceError(400, "invalid_request", f"{key} must be a nonempty string")
+ for key in ("rollout_id", "group_index"):
+ if type(request[key]) is not int or request[key] < 0:
+ raise ServiceError(400, "invalid_request", f"{key} must be a nonnegative integer")
+ task = request["task"]
+ if not isinstance(task, dict) or set(task) != TASK_ENTRY_KEYS:
+ raise ServiceError(400, "invalid_request",
+ f"task requires exactly {sorted(TASK_ENTRY_KEYS)}")
+ if not isinstance(task["reward_profile"], dict):
+ raise ServiceError(400, "invalid_request", "task.reward_profile must be a mapping")
+ sampling = request["sampling"]
+ if not isinstance(sampling, dict) or set(sampling) != SAMPLING_KEYS:
+ raise ServiceError(400, "invalid_request",
+ f"sampling requires exactly {sorted(SAMPLING_KEYS)}")
+ if not finite(sampling["temperature"]) or not finite(sampling["top_p"]):
+ raise ServiceError(400, "invalid_request", "sampling probabilities must be finite")
+ if type(sampling["top_k"]) is not int:
+ raise ServiceError(400, "invalid_request", "sampling.top_k must be an integer")
+ if type(sampling["max_new_tokens"]) is not int or sampling["max_new_tokens"] < 1:
+ raise ServiceError(400, "invalid_request", "sampling.max_new_tokens must be positive")
+ if (not isinstance(sampling["stop"], list)
+ or any(not isinstance(stop, str) or not stop for stop in sampling["stop"])):
+ raise ServiceError(400, "invalid_request", "sampling.stop must be a string list")
+ slots = request["slots"]
+ if (not isinstance(slots, list) or len(slots) < 2
+ or any(not isinstance(slot, dict) or set(slot) != {"slot_id"}
+ or not isinstance(slot["slot_id"], str) or not slot["slot_id"].strip()
+ for slot in slots)):
+ raise ServiceError(400, "invalid_request",
+ "slots must describe at least two unique slot ids")
+ if len({slot["slot_id"] for slot in slots}) != len(slots):
+ raise ServiceError(400, "invalid_request", "slot ids must be unique")
+ return request
+
+
+def request_digest(request):
+ """Canonical digest of the full request content, group id included."""
+ return digest(request)
+
+
+def manifest_path_from(run_root, identity):
+ """Deterministic trajectory manifest path for one identity."""
+ return Path(run_root) / "native-rollouts" / identity["group_id"] / \
+ identity["attempt_id"] / "trajectory.v2.json"
+
+
+def build_group_result(run_id, request, trajectories, *, runtime, export=None):
+ if len(trajectories) != len(request["slots"]):
+ raise ServiceError(500, "incomplete_group",
+ "executor returned a different slot count")
+ slots = []
+ for descriptor, ir in zip(request["slots"], trajectories):
+ identity = ir["identity"]
+ path = runtime.manifest_path(identity)
+ payload = read_json(path)
+ seal = ir["trace_seal"]
+ slots.append({
+ "slot_id": descriptor["slot_id"],
+ "trajectory_id": identity["trajectory_id"],
+ "attempt_id": identity["attempt_id"],
+ "manifest_path": str(path),
+ "manifest_sha256": digest(payload),
+ "finish_reason": ir["termination"],
+ "reward": ir["evaluation"]["training_reward"],
+ "policy_version": identity["policy_version"],
+ "task_digest": identity["task_digest"],
+ "harness_digest": identity["harness_digest"],
+ "sampling_digest": identity["sampling_digest"],
+ "readiness": ir["readiness"],
+ "integrity_errors": ir["integrity_errors"],
+ "trace_seal": {"status": seal["status"], "errors": seal["errors"],
+ "digest": digest(seal)},
+ "verifier_digest": ir["evaluation"].get("source_result_digest"),
+ })
+ result = {
+ "schema_version": SERVICE_SCHEMA,
+ "run_id": run_id,
+ "group_id": request["group_id"],
+ "rollout_id": request["rollout_id"],
+ "request_digest": request_digest(request),
+ "policy_version": request["policy_version"],
+ "status": "complete",
+ "slots": slots,
+ "completed_at": time.time(),
+ }
+ if export is not None:
+ result["group_export"] = export
+ return result
+
+
+async def default_executor(config, runtime_, request):
+ """The production executor: the framework-free rollout core."""
+ return await run_group(
+ config, runtime_, entry=request["task"], group_index=request["group_index"],
+ rollout_id=request["rollout_id"], sampling_digest=digest(request["sampling"]),
+ policy_version=request["policy_version"], group_size=len(request["slots"]))
+
+
+@dataclass
+class GroupState:
+ request: dict
+ request_sha: str
+ status: str = "queued"
+ result: dict = None
+ error: str = None
+ created_at: float = field(default_factory=time.time)
+ future: object = None
+
+ def summary(self, *, include_result=True):
+ payload = {"group_id": self.request["group_id"], "status": self.status,
+ "request_digest": self.request_sha,
+ "created_at": self.created_at}
+ if self.error:
+ payload["error"] = self.error
+ if include_result and self.result is not None:
+ payload["result"] = self.result
+ return payload
+
+
+@dataclass
+class RunState:
+ run_id: str
+ manifest: dict
+ manifest_sha: str
+ token_digest: str
+ config: object = None
+ runtime: object = None
+ drained: bool = False
+ serving: str = None
+ groups: dict = field(default_factory=dict)
+
+
+class RolloutService:
+ """Run registry plus a single-threaded asyncio executor for group work."""
+
+ def __init__(self, *, run_root, executor=None, runtime_factory=None,
+ token_file=None, group_wait_timeout=3600.0, poll_interval=0.05,
+ allow_synthetic=False):
+ self.run_root = Path(run_root).resolve()
+ self.executor = executor or default_executor
+ self.runtime_factory = runtime_factory
+ self.allow_synthetic = allow_synthetic
+ self.token = None
+ if token_file is not None:
+ token = Path(token_file).read_text().strip()
+ if not token:
+ raise ServiceError(500, "invalid_token_file", "run token file is empty")
+ self.token = token
+ self.group_wait_timeout = group_wait_timeout
+ self.poll_interval = poll_interval
+ self.runs = {}
+ self.lock = threading.RLock()
+ self.loop = asyncio.new_event_loop()
+ self.thread = threading.Thread(target=self._serve_loop,
+ name="harborrl-rollout-service", daemon=True)
+ self.thread.start()
+
+ def _serve_loop(self):
+ asyncio.set_event_loop(self.loop)
+ self.loop.run_forever()
+
+ def close(self):
+ with self.lock:
+ states = [state for state in self.runs.values() if state.runtime is not None]
+ for state in states:
+ try:
+ state.runtime.close()
+ except AttributeError:
+ pass
+ self.loop.call_soon_threadsafe(self.loop.stop)
+ self.thread.join(5)
+
+ # -- run lifecycle ----------------------------------------------------
+
+ def _token_digest(self, token):
+ return hashlib.sha256(token.encode()).hexdigest()
+
+ def register_run(self, run_id, manifest, *, token=None):
+ if not isinstance(run_id, str) or not run_id.strip():
+ raise ServiceError(400, "invalid_request", "run id required")
+ if not isinstance(manifest, dict) or not manifest:
+ raise ServiceError(400, "invalid_request", "manifest must be a nonempty mapping")
+ if manifest.get("run_id", run_id) != run_id:
+ raise ServiceError(400, "invalid_request", "manifest run id must match the path")
+ manifest_sha = digest(manifest)
+ with self.lock:
+ existing = self.runs.get(run_id)
+ if existing is not None:
+ if existing.manifest_sha != manifest_sha:
+ raise ServiceError(409, "conflict",
+ "run id already registered with a different manifest")
+ return {"run_id": run_id, "state": "registered", "existed": True}
+ if self.token is not None and token != self.token:
+ raise ServiceError(401, "unauthorized", "invalid manager run token")
+ generated = token is None
+ if generated:
+ token = secrets.token_urlsafe(32)
+ self.runs[run_id] = RunState(run_id=run_id, manifest=manifest,
+ manifest_sha=manifest_sha,
+ token_digest=self._token_digest(token))
+ return {"run_id": run_id, "state": "registered", "existed": False,
+ **({"run_token": token} if generated else {})}
+
+ def authenticate(self, run_id, token):
+ with self.lock:
+ state = self.runs.get(run_id)
+ if state is None:
+ raise ServiceError(404, "not_found", f"unknown run: {run_id}")
+ if not isinstance(token, str) or not token or not hmac.compare_digest(
+ state.token_digest, self._token_digest(token)):
+ raise ServiceError(401, "unauthorized", "invalid run token")
+ return state
+
+ def _run(self, run_id):
+ state = self.runs.get(run_id)
+ if state is None:
+ raise ServiceError(404, "not_found", f"unknown run: {run_id}")
+ return state
+
+ def attach_runtime(self, run_id, config, runtime):
+ """Bind the rollout runtime that executes this run's groups."""
+ with self.lock:
+ state = self._run(run_id)
+ state.config, state.runtime = config, runtime
+ return {"run_id": run_id, "state": "attached"}
+
+ def announce_serving(self, run_id, router_url):
+ parts = urlsplit(router_url or "")
+ if (parts.scheme not in ("http", "https") or not parts.hostname
+ or parts.username or parts.password or parts.query
+ or parts.fragment or parts.path not in ("", "/")):
+ raise ServiceError(400, "invalid_request", "router_url must be a plain origin")
+ with self.lock:
+ state = self._run(run_id)
+ state.serving = f"{parts.scheme}://{parts.netloc}".rstrip("/")
+ if state.runtime is None and self.runtime_factory is not None:
+ runtime = self.runtime_factory(run_id, state.serving)
+ state.runtime = runtime
+ if state.config is None:
+ # The factory-built runtime carries its own config; the
+ # executor needs both for group admission.
+ state.config = getattr(runtime, "config", None)
+ return {"run_id": run_id, "router_url": state.serving,
+ "runtime": "attached" if state.runtime is not None else "pending"}
+
+ # -- policy lifecycle (§7) ---------------------------------------------
+
+ def activate_policy(self, run_id, states, expected):
+ from harborrl.platform.policy_pool import validate_pool
+
+ with self.lock:
+ state = self._run(run_id)
+ if state.runtime is None:
+ raise ServiceError(409, "admission_closed", "run has no serving runtime yet")
+ try:
+ version = validate_pool(states, str(expected))
+ backend = getattr(state.runtime, 'backend', None)
+ if backend is not None:
+ live_states = backend.pool_states()
+ validate_pool(live_states, version)
+ if {item['endpoint'] for item in live_states} != {item['endpoint'] for item in states}:
+ raise ValueError('manager and GPU disagree on serving engine membership')
+ states = live_states
+ except (RuntimeError, TypeError, ValueError) as exc:
+ raise ServiceError(409, "pool_invalid", str(exc)) from exc
+ registry = state.runtime.registry
+ registry.drain(float(getattr(state.config, "drain_timeout", 30)))
+ registry.publish_version(states, version)
+ # Mirror policy_pool.publish_pool into the shared run directory so the
+ # GPU side can lock the same version without manager-local state; the
+ # record is replaceable because activation legitimately advances.
+ payload = {"weight_version": version, "engines": states, "timestamp": time.time()}
+ temp = self.run_root / "policy-pool.tmp"
+ temp.write_text(json.dumps(payload) + "\n")
+ temp.replace(self.run_root / "policy-pool.json")
+ return {"run_id": run_id, "policy_version": version, "admission": "open"}
+
+ # -- group lifecycle ----------------------------------------------------
+
+ def _group_dir(self, run_id, group_id):
+ return self.run_root / GROUP_DIR / run_id / group_id
+
+ def submit_group(self, run_id, request, *, wait=True):
+ request = validate_group_request(request)
+ sha = request_digest(request)
+ with self.lock:
+ state = self._run(run_id)
+ existing = state.groups.get(request["group_id"])
+ if existing is not None:
+ if existing.request_sha != sha:
+ raise ServiceError(409, "conflict",
+ "group id already submitted with different content")
+ group = existing
+ else:
+ if state.drained:
+ raise ServiceError(409, "admission_closed", "run is draining")
+ if state.runtime is None or state.config is None:
+ raise ServiceError(409, "admission_closed",
+ "run has no rollout runtime attached")
+ registry = getattr(state.runtime, "registry", None)
+ version = getattr(registry, "version", None) if registry is not None else None
+ accepting = getattr(registry, "accepting", True) if registry is not None else False
+ if not accepting or version != request["policy_version"]:
+ raise ServiceError(
+ 409, "stale_policy",
+ f"policy {request['policy_version']!r} is not the admitted version "
+ f"({version!r}, accepting={accepting}); activate first")
+ group = GroupState(request=request, request_sha=sha)
+ state.groups[request["group_id"]] = group
+ future = asyncio.run_coroutine_threadsafe(
+ self._execute(state, group), self.loop)
+ group.future = future
+ if wait:
+ return self._wait_group(run_id, group)
+ return group.summary(include_result=False)
+
+ def _wait_group(self, run_id, group):
+ deadline = time.monotonic() + self.group_wait_timeout
+ while group.status in ("queued", "running"):
+ if time.monotonic() >= deadline:
+ return {**group.summary(include_result=False),
+ "hint": "query GET /groups/{group_id}; do not resubmit"}
+ time.sleep(self.poll_interval)
+ return group.summary()
+
+ async def _execute(self, state, group):
+ # Immutable state records: each transition gets its own file, and
+ # publish() refuses to rewrite history.
+ request = group.request
+ directory = self._group_dir(state.run_id, request["group_id"])
+ publish(directory / "request.json", request)
+ group.status = "running"
+ publish(directory / "running.json", group.summary(include_result=False))
+ try:
+ trajectories = await self.executor(state.config, state.runtime, request)
+ export = export_group(
+ trajectories, len(trajectories),
+ allow_synthetic=self.allow_synthetic)
+ export_path = directory / "group-export.json"
+ publish(export_path, export)
+ group.result = build_group_result(
+ state.run_id, request, trajectories, runtime=state.runtime,
+ export={"path": str(export_path), "sha256": digest(export)})
+ group.status = "complete"
+ publish(directory / "result.json", group.result)
+ except asyncio.CancelledError:
+ group.status = "cancelled"
+ publish(directory / "cancelled.json", group.summary(include_result=False))
+ raise
+ except Exception as exc: # noqa: BLE001 — any executor failure is
+ # recorded as a failed group instead of killing the service
+ group.status = "failed"
+ group.error = f"{type(exc).__name__}: {exc}"
+ publish(directory / "failed.json", group.summary(include_result=False))
+
+ def group_status(self, run_id, group_id):
+ with self.lock:
+ state = self._run(run_id)
+ group = state.groups.get(group_id)
+ if group is None:
+ raise ServiceError(404, "not_found", f"unknown group: {group_id}")
+ return group.summary()
+
+ def cancel_group(self, run_id, group_id):
+ with self.lock:
+ state = self._run(run_id)
+ group = state.groups.get(group_id)
+ if group is None:
+ raise ServiceError(404, "not_found", f"unknown group: {group_id}")
+ self._cancel_groups([group])
+ return group.summary()
+
+ def drain_run(self, run_id):
+ with self.lock:
+ state = self._run(run_id)
+ state.drained = True
+ runtime = state.runtime
+ if runtime is not None:
+ registry = getattr(runtime, "registry", None)
+ if registry is not None:
+ timeout = float(getattr(state.config, "drain_timeout", 30))
+ try:
+ registry.drain(timeout)
+ except TimeoutError as exc:
+ raise ServiceError(409, "drain_timeout",
+ f"generation still in flight: {exc}") from exc
+ return {"run_id": run_id, "state": "drained"}
+
+ def cancel_run(self, run_id):
+ with self.lock:
+ state = self._run(run_id)
+ groups = list(state.groups.values())
+ self._cancel_groups(groups)
+ return {"run_id": run_id, "state": "cancelled",
+ "groups": {group.request["group_id"]: group.status for group in groups}}
+
+ def _cancel_groups(self, groups):
+ for group in groups:
+ if group.status in ("queued", "running") and group.future is not None:
+ group.future.cancel()
+ deadline = time.monotonic() + 30
+ while any(group.status in ("queued", "running") for group in groups) \
+ and time.monotonic() < deadline:
+ time.sleep(self.poll_interval)
+
+
+def make_control_server(service, host="127.0.0.1", port=0, *, max_body_bytes=8 * 1024 * 1024):
+ """Bounded stdlib HTTP transport for the control plane."""
+
+ class Handler(BaseHTTPRequestHandler):
+ def setup(self):
+ super().setup()
+ self.connection.settimeout(120)
+
+ def log_message(self, *args):
+ pass
+
+ def reply(self, status, payload):
+ body = json.dumps(payload).encode()
+ self.send_response(status)
+ self.send_header("Content-Type", "application/json")
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ def token(self):
+ token = self.headers.get("x-api-key", "")
+ auth = self.headers.get("Authorization", "")
+ if auth:
+ if not auth.startswith("Bearer ") or token:
+ raise ServiceError(400, "invalid_request", "ambiguous authentication")
+ token = auth[7:]
+ return token
+
+ def read_json(self):
+ if self.headers.get("Transfer-Encoding"):
+ raise ServiceError(400, "invalid_request", "chunked bodies are not supported")
+ size = int(self.headers.get("Content-Length", "0"))
+ if size <= 0 or size > max_body_bytes:
+ raise ServiceError(400, "invalid_request", "invalid request body size")
+ return json.loads(self.rfile.read(size))
+
+ def dispatch(self, method):
+ parts = [part for part in urlsplit(self.path).path.split("/") if part]
+ try:
+ payload = self.route(method, parts)
+ except ServiceError as exc:
+ self.reply(exc.status, {"error": {"type": exc.code, "message": str(exc)}})
+ except (ValueError, TypeError, KeyError, OSError, json.JSONDecodeError) as exc:
+ self.reply(400, {"error": {"type": "invalid_request",
+ "message": f"{type(exc).__name__}: {exc}"}})
+ else:
+ self.reply(200, payload)
+
+ def route(self, method, parts):
+ if method == "GET" and parts == ["healthz"]:
+ return {"ok": True, "service": SERVICE_SCHEMA}
+ if method == "GET" and parts == ["readyz"]:
+ with service.lock:
+ attached = sum(1 for state in service.runs.values()
+ if state.runtime is not None)
+ return {"ok": True, "runs": len(service.runs), "attached_runs": attached}
+ if len(parts) < 2 or parts[0] != "runs":
+ raise ServiceError(404, "not_found", "unsupported endpoint")
+ run_id = parts[1]
+ if len(parts) == 3 and method == "POST" and parts[2] == "register":
+ body = self.read_json()
+ if not isinstance(body, dict) or set(body) != {"manifest"}:
+ raise ServiceError(400, "invalid_request", "body must be {manifest}")
+ return service.register_run(run_id, body["manifest"], token=self.token())
+ service.authenticate(run_id, self.token())
+ if len(parts) == 3 and method == "POST" and parts[2] == "groups":
+ return service.submit_group(run_id, self.read_json())
+ if (len(parts) == 4 and parts[2] == "groups"
+ and method == "GET"):
+ return service.group_status(run_id, parts[3])
+ if (len(parts) == 5 and parts[2] == "groups" and parts[4] == "cancel"
+ and method == "POST"):
+ return service.cancel_group(run_id, parts[3])
+ if len(parts) == 4 and method == "POST" and parts[2] == "policy" \
+ and parts[3] == "activate":
+ body = self.read_json()
+ if not isinstance(body, dict) or set(body) != {"states", "expected"}:
+ raise ServiceError(400, "invalid_request",
+ "body must be {states, expected}")
+ return service.activate_policy(run_id, body["states"], body["expected"])
+ if len(parts) == 4 and method == "POST" and parts[2] == "serving" \
+ and parts[3] == "announce":
+ body = self.read_json()
+ if not isinstance(body, dict) or set(body) != {"router_url"}:
+ raise ServiceError(400, "invalid_request", "body must be {router_url}")
+ return service.announce_serving(run_id, body["router_url"])
+ if len(parts) == 3 and method == "POST" and parts[2] == "drain":
+ return service.drain_run(run_id)
+ if len(parts) == 3 and method == "POST" and parts[2] == "cancel":
+ return service.cancel_run(run_id)
+ raise ServiceError(404, "not_found", "unsupported endpoint")
+
+ def do_GET(self):
+ self.dispatch("GET")
+
+ def do_POST(self):
+ self.dispatch("POST")
+
+ server = ThreadingHTTPServer((host, port), Handler)
+ server.daemon_threads = True
+ return server
diff --git a/harborrl/tasks/catalog.py b/harborrl/tasks/catalog.py
new file mode 100644
index 000000000..d887e7ec6
--- /dev/null
+++ b/harborrl/tasks/catalog.py
@@ -0,0 +1,8 @@
+"""Compatibility entry point for the selected Harbor task provider."""
+from __future__ import annotations
+
+from harborrl.math_registry import select_math
+
+
+def catalog(config):
+ return select_math(config).task.adapter.catalog(config)
diff --git a/harborrl/tasks/cli.py b/harborrl/tasks/cli.py
index 8cbb3c5d3..f9d337cd3 100644
--- a/harborrl/tasks/cli.py
+++ b/harborrl/tasks/cli.py
@@ -7,7 +7,7 @@
from pathlib import Path
from uuid import uuid4
-from harborrl.data.harbor.native_inspector import inspect_native
+from harborrl.math_registry import NATIVE_TASK_PROVIDER
def main(argv=None):
@@ -23,12 +23,12 @@ def main(argv=None):
if args.command == "inspect":
if not args.tasks_dir.is_dir():
parser.error("tasks-dir must exist")
- tasks = sorted(path.parent for path in args.tasks_dir.rglob("task.toml"))
+ tasks = NATIVE_TASK_PROVIDER.adapter.discover(args.tasks_dir)
if not tasks:
parser.error("no task.toml found")
rows = []
for task in tasks:
- result = inspect_native(task)
+ result = NATIVE_TASK_PROVIDER.adapter.inspect(task)
rows.append({
**result,
"dataset": args.dataset,
diff --git a/harborrl/tasks/content.py b/harborrl/tasks/content.py
new file mode 100644
index 000000000..e108f167e
--- /dev/null
+++ b/harborrl/tasks/content.py
@@ -0,0 +1,26 @@
+"""Native task content hashing and TOML reader shared with compatibility tools."""
+import hashlib
+from pathlib import Path
+
+try:
+ import tomllib
+except ModuleNotFoundError: # Python 3.10
+ import tomli as tomllib
+
+
+__all__ = ["tomllib", "tree_digest"]
+
+
+def tree_digest(root: Path, *, exclude: tuple[str, ...] = ()) -> str:
+ h = hashlib.sha256()
+ for path in sorted(Path(root).rglob("*")):
+ if str(path.relative_to(root)) in exclude:
+ continue
+ if path.is_symlink():
+ raise ValueError("symlinks are not supported")
+ if path.is_file():
+ data = path.read_bytes()
+ h.update(str(path.relative_to(root)).encode() + b"\0")
+ h.update(str(path.stat().st_mode & 0o777).encode() + b"\0")
+ h.update(str(len(data)).encode() + b"\0" + data)
+ return h.hexdigest()
diff --git a/harborrl/tasks/jsonl_provider.py b/harborrl/tasks/jsonl_provider.py
new file mode 100644
index 000000000..4fdeae0fd
--- /dev/null
+++ b/harborrl/tasks/jsonl_provider.py
@@ -0,0 +1,92 @@
+"""JSONL Harbor task catalog provider with the same native task lock contract."""
+from __future__ import annotations
+
+import json
+import re
+from pathlib import Path
+
+from harborrl.data.harbor.native_inspector import inspect_native
+from harborrl.trajectories.native import RewardProfile
+
+
+class JsonlTaskProvider:
+ provider_id = "harbor_jsonl"
+
+ def discover(self, tasks_dir):
+ return sorted(path.parent for path in Path(tasks_dir).rglob("task.toml"))
+
+ def inspect(self, path, *, expected_digest=None):
+ return inspect_native(path, expected_digest=expected_digest)
+
+ def catalog(self, config):
+ path = Path(config["tasks"]["catalog"])
+ entries, seen = [], set()
+ with path.open(encoding="utf-8") as source:
+ for line_number, line in enumerate(source, start=1):
+ if not line.strip():
+ raise ValueError(f"blank JSONL catalog line: {line_number}")
+ try:
+ entry = json.loads(line)
+ except json.JSONDecodeError as exc:
+ raise ValueError(
+ f"invalid JSONL catalog line {line_number}: {exc.msg}"
+ ) from exc
+ if (
+ not isinstance(entry, dict)
+ or set(entry)
+ != {"id", "revision", "path", "task_digest", "reward_profile"}
+ or not isinstance(entry["id"], str)
+ or not entry["id"].strip()
+ or not isinstance(entry["revision"], str)
+ or not entry["revision"].strip()
+ or entry["id"] in seen
+ ):
+ raise ValueError(
+ f"invalid or duplicate JSONL catalog identity: {line_number}"
+ )
+ if (
+ not isinstance(entry["path"], str)
+ or not entry["path"].strip()
+ or not isinstance(entry["task_digest"], str)
+ or not re.fullmatch(r"[0-9a-f]{64}", entry["task_digest"])
+ or not isinstance(entry["reward_profile"], dict)
+ ):
+ raise ValueError(f'invalid native task lock: {entry["id"]}')
+ seen.add(entry["id"])
+ entry["path"] = str((path.parent / entry["path"]).resolve())
+ report = self.inspect(
+ entry["path"], expected_digest=entry["task_digest"]
+ )
+ if report["status"] != "NEEDS_PROBE":
+ raise ValueError(
+ f'task preflight failed: {entry["id"]}: {report["reasons"]}'
+ )
+ RewardProfile(**entry["reward_profile"])
+ entries.append(entry)
+ if not entries:
+ raise ValueError("catalog must contain at least one JSONL task lock")
+ selected = set(getattr(config, "task_ids", ()))
+ if selected:
+ missing = selected - {entry["id"] for entry in entries}
+ if missing:
+ raise ValueError(
+ f"task selector not found in catalog: {min(missing)}"
+ )
+ entries = [entry for entry in entries if entry["id"] in selected]
+ return entries
+
+ def prompt_rows(self, entries):
+ rows = []
+ for entry in entries:
+ instruction = (Path(entry["path"]) / "instruction.md").read_text()
+ rows.append(
+ {
+ "task": instruction,
+ "metadata": {
+ "harborrl_native": True,
+ "task_provider": self.provider_id,
+ "native_task": entry,
+ },
+ }
+ )
+ return rows
diff --git a/harborrl/tasks/provider.py b/harborrl/tasks/provider.py
new file mode 100644
index 000000000..239fd58ef
--- /dev/null
+++ b/harborrl/tasks/provider.py
@@ -0,0 +1,72 @@
+"""Native Harbor task discovery, locking, and prompt materialization."""
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+from harborrl.data.harbor.native_inspector import inspect_native
+from harborrl.trajectories.native import RewardProfile, read_json
+
+
+class NativeTaskProvider:
+ provider_id = "native_harbor"
+
+ def discover(self, tasks_dir):
+ return sorted(path.parent for path in Path(tasks_dir).rglob("task.toml"))
+
+ def inspect(self, path, *, expected_digest=None):
+ return inspect_native(path, expected_digest=expected_digest)
+
+ def catalog(self, config):
+ path = Path(config["tasks"]["catalog"])
+ entries = read_json(path)
+ if not isinstance(entries, list) or not entries:
+ raise ValueError("catalog must be a nonempty JSON list")
+ seen = set()
+ for entry in entries:
+ if (
+ not isinstance(entry, dict)
+ or set(entry)
+ != {"id", "revision", "path", "task_digest", "reward_profile"}
+ or not isinstance(entry["id"], str)
+ or not entry["id"].strip()
+ or not isinstance(entry["revision"], str)
+ or not entry["revision"].strip()
+ or entry["id"] in seen
+ ):
+ raise ValueError("invalid or duplicate catalog identity")
+ if (
+ not isinstance(entry["path"], str)
+ or not entry["path"].strip()
+ or not isinstance(entry["task_digest"], str)
+ or not re.fullmatch(r"[0-9a-f]{64}", entry["task_digest"])
+ or not isinstance(entry["reward_profile"], dict)
+ ):
+ raise ValueError(f'invalid native task lock: {entry["id"]}')
+ seen.add(entry["id"])
+ entry["path"] = str((path.parent / entry["path"]).resolve())
+ report = self.inspect(entry["path"], expected_digest=entry["task_digest"])
+ if report["status"] != "NEEDS_PROBE":
+ raise ValueError(
+ f'task preflight failed: {entry["id"]}: {report["reasons"]}'
+ )
+ RewardProfile(**entry["reward_profile"])
+ selected = set(getattr(config, "task_ids", ()))
+ if selected:
+ missing = selected - {entry["id"] for entry in entries}
+ if missing:
+ raise ValueError(f"task selector not found in catalog: {min(missing)}")
+ entries = [entry for entry in entries if entry["id"] in selected]
+ return entries
+
+ def prompt_rows(self, entries):
+ rows = []
+ for entry in entries:
+ instruction = (Path(entry["path"]) / "instruction.md").read_text()
+ rows.append(
+ {
+ "task": instruction,
+ "metadata": {"harborrl_native": True, "native_task": entry},
+ }
+ )
+ return rows
diff --git a/harborrl/webui/__init__.py b/harborrl/webui/__init__.py
new file mode 100644
index 000000000..765ec5b8e
--- /dev/null
+++ b/harborrl/webui/__init__.py
@@ -0,0 +1 @@
+"""Local configuration and training console; runtime imports stay lazy."""
diff --git a/harborrl/webui/cli.py b/harborrl/webui/cli.py
new file mode 100644
index 000000000..1193ce5d8
--- /dev/null
+++ b/harborrl/webui/cli.py
@@ -0,0 +1,67 @@
+"""Start a single-user loopback console, optionally opening the browser."""
+import argparse
+import contextlib
+import os
+import signal
+import webbrowser
+from pathlib import Path
+
+
+@contextlib.contextmanager
+def workspace_lock(workspace):
+ workspace.mkdir(parents=True, exist_ok=True)
+ with (workspace / ".console.lock").open("a+b") as stream:
+ try:
+ if os.name == "posix":
+ import fcntl
+ fcntl.flock(stream.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ else:
+ import msvcrt
+ stream.seek(0)
+ if stream.read(1) == b"":
+ stream.write(b"0")
+ stream.flush()
+ stream.seek(0)
+ msvcrt.locking(stream.fileno(), msvcrt.LK_NBLCK, 1)
+ except OSError as exc:
+ raise ValueError("Another console owns this workspace") from exc
+ yield
+
+
+def main(argv):
+ parser = argparse.ArgumentParser(prog="harborrl web", description=__doc__)
+ parser.add_argument("--workspace", default="./harborrl-web", help="Configurations and job logs directory")
+ parser.add_argument("--port", type=int, default=0, help="Loopback port; 0 selects a free port")
+ parser.add_argument("--no-browser", action="store_true", help="Print the URL without opening a browser")
+ args = parser.parse_args(argv)
+ if not 0 <= args.port <= 65535:
+ parser.error("port must be in 0..65535")
+ from .server import Console, make_server
+
+ workspace = Path(args.workspace).expanduser().resolve()
+ try:
+ with workspace_lock(workspace):
+ console = Console(workspace)
+ server = make_server(console, args.port)
+ url = f"http://127.0.0.1:{server.server_port}/#token={console.token}"
+ print(f"HarborRL console: {url}\nWorkspace: {workspace}\nKeep this terminal open. Ctrl+C stops the console and requests active job cleanup.", flush=True)
+ if not args.no_browser:
+ try:
+ webbrowser.open(url)
+ except webbrowser.Error:
+ print("Browser unavailable; open the printed URL.", flush=True)
+ previous = signal.getsignal(signal.SIGTERM)
+ def terminate(_signum, _frame):
+ raise KeyboardInterrupt
+ signal.signal(signal.SIGTERM, terminate)
+ try:
+ server.serve_forever()
+ except KeyboardInterrupt:
+ pass
+ finally:
+ server.server_close()
+ console.jobs.close()
+ signal.signal(signal.SIGTERM, previous)
+ except (OSError, ValueError) as exc:
+ parser.error(str(exc))
+ return 0
diff --git a/harborrl/webui/jobs.py b/harborrl/webui/jobs.py
new file mode 100644
index 000000000..28a36df2b
--- /dev/null
+++ b/harborrl/webui/jobs.py
@@ -0,0 +1,173 @@
+"""One foreground CLI job at a time, with immutable inputs and durable logs."""
+import json
+import os
+import signal
+import subprocess
+import threading
+from datetime import datetime, timezone
+from pathlib import Path
+from uuid import uuid4
+
+import yaml
+
+from harborrl.options.wizard import build_config_bundle, publish_bundle
+
+
+def now():
+ return datetime.now(timezone.utc).isoformat()
+
+
+class Jobs:
+ def __init__(self, root):
+ self.root = Path(root)
+ self.root.mkdir(parents=True, exist_ok=True)
+ self.lock = threading.RLock()
+ self.child = None
+ self.active = None
+ self.closed = False
+ self.records = {}
+ for path in self.root.glob("*/job.json"):
+ record = json.loads(path.read_text(encoding="utf-8"))
+ if record["state"] in {"running", "stopping"}:
+ record.update(state="unknown", message="Console restarted; verify target cleanup before launching again.")
+ self.records[record["id"]] = record
+
+ def _save(self, record):
+ path = self.root / record["id"] / "job.json"
+ temporary = path.with_suffix(".tmp")
+ temporary.write_text(json.dumps(record, indent=2) + "\n", encoding="utf-8")
+ temporary.replace(path)
+
+ def _refresh(self):
+ if self.child is not None and self.child.poll() is not None:
+ record = self.records[self.active]
+ code = self.child.returncode
+ record.update(state="stopped" if record["state"] == "stopping" else "succeeded" if code == 0 else "failed",
+ exit_code=code, finished_at=now())
+ self._save(record)
+ self.child = self.active = None
+
+ def _watch(self, child):
+ child.wait()
+ with self.lock:
+ if self.child is child:
+ self._refresh()
+
+ def list(self):
+ with self.lock:
+ self._refresh()
+ return [dict(record) for record in sorted(self.records.values(), key=lambda r: r["created_at"], reverse=True)]
+
+ def start(self, configuration, bundle, mode):
+ if mode not in {"dry-run", "doctor", "train"}:
+ raise ValueError("Unknown run mode")
+ with self.lock:
+ self._refresh()
+ if self.closed or self.child is not None:
+ raise ValueError("A job is already active or the console is closing")
+ if any(record["state"] == "unknown" for record in self.records.values()):
+ raise ValueError("An interrupted job has unknown cleanup status. Reconcile it before launching again.")
+ identifier = uuid4().hex
+ root = self.root / identifier
+ root.mkdir(mode=0o700)
+ training = yaml.safe_load(bundle["files"]["config.yaml"])
+ deployment = yaml.safe_load(bundle["files"]["deployment.yaml"])
+ manifest = json.loads(bundle["files"]["setup-manifest.json"])
+ output = str(Path(training["output"]["root"]) / "web" / identifier)
+ training["output"]["root"] = deployment["storage"]["run_root"] = output
+ snapshot = build_config_bundle({"answers_version": 1, "preset": manifest["preset"],
+ "training": training, "deployment": deployment}, output_directory=root / "inputs")
+ publish_bundle(snapshot, root / "inputs")
+ manager = deployment["manager"]
+ command = [manager["controller_python"], "-u", "-m", "harborrl.cli", "doctor" if mode == "doctor" else "train",
+ str(root / "inputs/config.yaml"), "--deployment", str(root / "inputs/deployment.yaml")]
+ if mode == "dry-run":
+ command.append("--dry-run")
+ record = {"id": identifier, "configuration": configuration, "mode": mode, "state": "running",
+ "created_at": now(), "finished_at": None, "exit_code": None, "command": command,
+ "log_path": str(root / "console.log"), "snapshot": str(root / "inputs"), "output_root": output}
+ self._save(record)
+ env = {**os.environ, "PYTHONUNBUFFERED": "1"}
+ env["PYTHONPATH"] = os.pathsep.join(filter(None, [manager["source_dir"], env.get("PYTHONPATH")]))
+ try:
+ with (root / "console.log").open("wb") as log:
+ self.child = subprocess.Popen(command, cwd=manager["source_dir"], env=env, stdin=subprocess.DEVNULL,
+ stdout=log, stderr=subprocess.STDOUT, start_new_session=True)
+ except OSError as exc:
+ record.update(state="failed", message=str(exc), finished_at=now())
+ else:
+ self.active = identifier
+ record["pid"] = self.child.pid
+ self.records[identifier] = record
+ self._save(record)
+ if self.child is not None:
+ threading.Thread(target=self._watch, args=(self.child,), name="harborrl-web-job", daemon=True).start()
+ return dict(record)
+
+ def stop(self, identifier):
+ with self.lock:
+ self._refresh()
+ if identifier != self.active or self.child is None:
+ raise ValueError("This job is not active")
+ record = self.records[identifier]
+ if record["state"] != "stopping":
+ # The existing CLI forwards signals to its local/SSH training target.
+ try:
+ self.child.send_signal(signal.SIGINT) if os.name == "posix" else self.child.terminate()
+ except ProcessLookupError:
+ self._refresh()
+ return dict(record)
+ record["state"] = "stopping"
+ self._save(record)
+ return dict(record)
+
+ def reconcile(self, identifier):
+ with self.lock:
+ record = self.records[identifier]
+ if record["state"] != "unknown":
+ raise ValueError("Only an interrupted job can be reconciled")
+ record.update(state="interrupted", finished_at=now(), message="Target cleanup confirmed by operator.")
+ self._save(record)
+ return dict(record)
+
+ def log(self, identifier):
+ with self.lock:
+ self._refresh()
+ record = self.records[identifier]
+ path = self.root / identifier / "console.log"
+ with path.open("rb") as stream:
+ size = stream.seek(0, os.SEEK_END)
+ stream.seek(max(0, size - 65536))
+ tail = stream.read(65536).decode("utf-8", errors="replace")
+ result = {"job": dict(record), "text": tail, "bytes": size, "truncated": size > 65536,
+ "training_text": "", "training_log_path": None, "training_truncated": False}
+ output = Path(record["output_root"])
+ candidates = sorted(output.glob("training/*/train-stdout.log"))
+ if candidates:
+ training_log = candidates[-1]
+ if training_log.resolve().is_relative_to(output.resolve()):
+ with training_log.open("rb") as stream:
+ length = stream.seek(0, os.SEEK_END)
+ stream.seek(max(0, length - 65536))
+ result.update(training_text=stream.read(65536).decode("utf-8", errors="replace"),
+ training_log_path=str(training_log), training_truncated=length > 65536)
+ return result
+
+ def close(self):
+ with self.lock:
+ self.closed = True
+ self._refresh()
+ child = self.child
+ if child:
+ self.stop(self.active)
+ if child:
+ try:
+ child.wait(timeout=30)
+ except subprocess.TimeoutExpired:
+ with self.lock:
+ record = self.records[self.active]
+ record.update(state="unknown", message="Graceful shutdown timed out; target cleanup must be checked.")
+ self._save(record)
+ else:
+ with self.lock:
+ self._refresh()
diff --git a/harborrl/webui/server.py b/harborrl/webui/server.py
new file mode 100644
index 000000000..442699175
--- /dev/null
+++ b/harborrl/webui/server.py
@@ -0,0 +1,189 @@
+"""Loopback-only HTTP boundary for the existing configuration services."""
+import hmac
+import json
+import os
+import re
+import secrets
+import threading
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from importlib.resources import files
+from pathlib import Path
+from urllib.parse import urlsplit
+
+import yaml
+
+from harborrl.options.catalog import build_catalog
+from harborrl.options.presets import PRESETS, defaults
+from harborrl.options.validation import read_mapping, validate_mappings
+from harborrl.options.wizard import build_config_bundle, publish_bundle
+
+from .jobs import Jobs
+
+ASSETS = {"/": ("index.html", "text/html; charset=utf-8"),
+ "/app.js": ("app.js", "text/javascript; charset=utf-8"),
+ "/style.css": ("style.css", "text/css; charset=utf-8"),
+ "/lucide.min.js": ("lucide.min.js", "text/javascript; charset=utf-8"),
+ "/brand.jpg": ("brand.jpg", "image/jpeg")}
+NAME = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}")
+
+
+class Console:
+ def __init__(self, workspace):
+ self.workspace = Path(workspace).expanduser().resolve()
+ self.configurations = self.workspace / "configurations"
+ self.configurations.mkdir(parents=True, exist_ok=True)
+ self.jobs = Jobs(self.workspace / "jobs")
+ self.token = secrets.token_urlsafe(32)
+ self.lock = threading.RLock()
+
+ def directory(self, name):
+ if not isinstance(name, str) or not NAME.fullmatch(name):
+ raise ValueError("Name must be 1-64 letters, digits, underscores or hyphens")
+ path = self.configurations / name
+ if path.is_symlink() or path.resolve().parent != self.configurations:
+ raise ValueError("Configuration must stay inside the workspace")
+ return path
+
+ def bootstrap(self):
+ return {"workspace": str(self.workspace), "catalog": build_catalog(),
+ "live_supported": os.name == "posix",
+ "defaults": {item["id"]: dict(zip(("training", "deployment"),
+ defaults(item["id"], self.configurations / "experiment"))) for item in PRESETS},
+ "configurations": self.list_configurations(), "jobs": self.jobs.list()}
+
+ def list_configurations(self):
+ result = []
+ for path in sorted(self.configurations.iterdir()):
+ if path.is_dir() and not path.is_symlink() and NAME.fullmatch(path.name) and (path / "setup-manifest.json").is_file():
+ result.append({"id": path.name, "path": str(path)})
+ return result
+
+ def preview(self, body):
+ output = self.directory(body["name"])
+ bundle = build_config_bundle(body["answers"], output_directory=output)
+ if body.get("check_files") is True:
+ bundle["validation"] = validate_mappings(yaml.safe_load(bundle["files"]["config.yaml"]),
+ yaml.safe_load(bundle["files"]["deployment.yaml"]), config_path=output / "config.yaml", check_files=True)
+ return bundle
+
+ def save(self, body):
+ with self.lock:
+ output = self.directory(body["name"])
+ bundle = build_config_bundle(body["answers"], output_directory=output)
+ publish_bundle(bundle, output)
+ return {"id": output.name, "path": str(output), "validation": bundle["validation"]}
+
+ def configuration(self, identifier):
+ root = self.directory(identifier)
+ manifest = json.loads((root / "setup-manifest.json").read_text(encoding="utf-8"))
+ answers = {"answers_version": 1, "preset": manifest["preset"],
+ "training": read_mapping(root / "config.yaml"), "deployment": read_mapping(root / "deployment.yaml")}
+ return {"id": identifier, "answers": answers,
+ "files": {name: (root / name).read_text(encoding="utf-8") for name in
+ ("config.yaml", "deployment.yaml", "setup-manifest.json")}}
+
+ def start(self, body):
+ mode = body["mode"]
+ if mode not in {"dry-run", "doctor", "train"}:
+ raise ValueError("Unknown run mode")
+ if mode != "dry-run" and (body.get("confirmed") is not True or os.name != "posix"):
+ raise ValueError("Live jobs require explicit confirmation and a Linux/POSIX controller")
+ with self.lock:
+ identifier = body["configuration"]
+ root = self.directory(identifier)
+ config = self.configuration(identifier)
+ result = validate_mappings(yaml.safe_load(config["files"]["config.yaml"]),
+ yaml.safe_load(config["files"]["deployment.yaml"]), config_path=root / "config.yaml", check_files=True)
+ if result["status"] == "invalid":
+ raise ValueError("; ".join(row["message"] for row in result["checks"] if row["status"] == "failed"))
+ return self.jobs.start(identifier, config, mode)
+
+
+def make_server(console, port=0):
+ class Handler(BaseHTTPRequestHandler):
+ def setup(self):
+ super().setup()
+ self.connection.settimeout(15)
+
+ def log_message(self, *_args):
+ pass
+
+ def respond(self, status, payload, content_type="application/json; charset=utf-8"):
+ data = payload if isinstance(payload, bytes) else json.dumps(payload).encode("utf-8")
+ self.send_response(status)
+ for name, value in {"Content-Type": content_type, "Content-Length": str(len(data)),
+ "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff",
+ "Referrer-Policy": "no-referrer", "X-Frame-Options": "DENY",
+ "Content-Security-Policy": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'"}.items():
+ self.send_header(name, value)
+ self.end_headers()
+ self.wfile.write(data)
+
+ def dispatch(self, method):
+ try:
+ host = self.headers.get("Host", "")
+ if urlsplit("http://" + host).hostname not in {"127.0.0.1", "localhost"}:
+ return self.respond(403, {"error": "Loopback Host required"})
+ path = urlsplit(self.path).path
+ if method == "GET" and path in ASSETS:
+ name, mime = ASSETS[path]
+ return self.respond(200, files("harborrl.webui").joinpath("static", name).read_bytes(), mime)
+ if not hmac.compare_digest(self.headers.get("Authorization", ""), "Bearer " + console.token):
+ return self.respond(403, {"error": "Console session expired or not authorized. Reopen the CLI URL."})
+ if method == "GET":
+ if path == "/api/bootstrap":
+ return self.respond(200, console.bootstrap())
+ if path == "/api/jobs":
+ return self.respond(200, console.jobs.list())
+ if path.startswith("/api/jobs/"):
+ return self.respond(200, console.jobs.log(path.removeprefix("/api/jobs/")))
+ if path.startswith("/api/configurations/"):
+ return self.respond(200, console.configuration(path.removeprefix("/api/configurations/")))
+ if method == "POST":
+ if self.headers.get("Origin") != "http://" + host:
+ return self.respond(403, {"error": "Same-origin request required"})
+ if self.headers.get("Content-Type", "").split(";")[0] != "application/json":
+ return self.respond(415, {"error": "JSON body required"})
+ size = int(self.headers.get("Content-Length", "0"))
+ if not 0 < size <= 1048576:
+ return self.respond(413, {"error": "Request must be between 1 byte and 1 MiB"})
+ body = json.loads(self.rfile.read(size))
+ if not isinstance(body, dict):
+ raise TypeError("Request must be an object")
+ if path == "/api/preview":
+ return self.respond(200, console.preview(body))
+ if path == "/api/configurations":
+ return self.respond(201, console.save(body))
+ if path == "/api/jobs":
+ return self.respond(201, console.start(body))
+ if path == "/api/stop":
+ return self.respond(200, console.jobs.stop(body["id"]))
+ if path == "/api/reconcile":
+ if body.get("confirmed") is not True:
+ raise ValueError("Confirm target cleanup before reconciling")
+ return self.respond(200, console.jobs.reconcile(body["id"]))
+ if path == "/api/import":
+ answers = yaml.safe_load(body["text"])
+ if (not isinstance(answers, dict) or type(answers.get("answers_version")) is not int
+ or answers.get("answers_version") != 1):
+ raise ValueError("Import an answers_version: 1 YAML/JSON mapping")
+ if set(answers) - {"answers_version", "preset", "training", "deployment", "sandbox_config"}:
+ raise ValueError("Unknown answer fields")
+ if any(not isinstance(answers.get(key, {}), dict) for key in ("training", "deployment")):
+ raise ValueError("training and deployment answers must be objects")
+ if answers.get("preset") not in {p["id"] for p in PRESETS}:
+ raise ValueError("Unknown deployment preset")
+ return self.respond(200, {"answers": answers})
+ self.respond(404, {"error": "Not found"})
+ except (ValueError, TypeError, KeyError, OSError, AttributeError, yaml.YAMLError) as exc:
+ self.respond(400, {"error": str(exc)})
+
+ def do_GET(self):
+ self.dispatch("GET")
+
+ def do_POST(self):
+ self.dispatch("POST")
+
+ server = ThreadingHTTPServer(("127.0.0.1", port), Handler)
+ server.daemon_threads = True
+ return server
diff --git a/harborrl/webui/static/app.js b/harborrl/webui/static/app.js
new file mode 100644
index 000000000..0a6a5dc0d
--- /dev/null
+++ b/harborrl/webui/static/app.js
@@ -0,0 +1,884 @@
+/* The form edits answers; Python remains the authoritative builder and validator. */
+"use strict";
+const $ = (selector) => document.querySelector(selector);
+const $$ = (selector) => [...document.querySelectorAll(selector)];
+const escapeHtml = (value) =>
+ String(value ?? "").replace(
+ /[&<>"']/g,
+ (c) =>
+ ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[
+ c
+ ],
+ );
+const icon = (name) => ` `;
+const state = {
+ bootstrap: null,
+ answers: null,
+ step: 0,
+ saved: null,
+ preview: null,
+ file: "config.yaml",
+ view: "configuration",
+ selectedJob: null,
+ logSource: "console",
+ jobs: [],
+ busy: false,
+};
+const fragment = new URLSearchParams(location.hash.slice(1));
+if (fragment.has("token")) {
+ sessionStorage.setItem("harborrl-token", fragment.get("token"));
+ history.replaceState(null, "", location.pathname);
+}
+const token = sessionStorage.getItem("harborrl-token") || "";
+function icons() {
+ lucide.createIcons({ attrs: { "aria-hidden": "true" } });
+}
+function notify(text, success = false) {
+ $("#notice").textContent = text;
+ $("#notice").className = `notice${success ? " success" : ""}`;
+ $("#notice").hidden = false;
+}
+async function api(path, body) {
+ const response = await fetch(path, {
+ method: body === undefined ? "GET" : "POST",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
+ });
+ const result = await response.json();
+ if (!response.ok) throw new Error(result.error || "Request failed");
+ return result;
+}
+async function action(callback) {
+ if (state.busy) return;
+ state.busy = true;
+ $("#notice").hidden = true;
+ try {
+ await callback();
+ } catch (error) {
+ notify(error.message);
+ } finally {
+ state.busy = false;
+ updateActions();
+ icons();
+ }
+}
+function valueAt(object, path) {
+ return path.split(".").reduce((value, key) => value?.[key], object);
+}
+function setAt(object, path, value) {
+ const parts = path.split(".");
+ let parent = object;
+ parts.slice(0, -1).forEach((key) => {
+ if (!parent[key] || typeof parent[key] !== "object") parent[key] = {};
+ parent = parent[key];
+ });
+ parent[parts.at(-1)] = value;
+}
+function defaultRoot() {
+ const separator = state.bootstrap.workspace.includes("\\") ? "\\" : "/";
+ return [
+ state.bootstrap.workspace,
+ "configurations",
+ $("#config-name").value,
+ "runs",
+ ].join(separator);
+}
+function read(path) {
+ const supplied = valueAt(state.answers, path);
+ if (supplied !== undefined) return supplied;
+ if (path === "sandbox_config")
+ return valueAt(state.answers, "training.harbor.environment.config");
+ if (path === "deployment.storage.run_root")
+ return valueAt(state.answers, "training.output.root") ?? defaultRoot();
+ if (
+ [
+ "training.deployment.actor_gpus",
+ "training.deployment.rollout_gpus",
+ ].includes(path) &&
+ read("training.deployment.layout") === "colocate"
+ )
+ return read("training.deployment.num_gpus");
+ return valueAt(state.bootstrap.defaults[state.answers.preset], path);
+}
+function changed() {
+ state.saved = null;
+ state.preview = null;
+ $("#draft-state").textContent = "Draft";
+ $("#draft-state").className = "badge";
+ updateActions();
+ renderSummary();
+}
+function deriveWorkers() {
+ if (state.answers.preset === "service-ssh") {
+ delete state.answers.training.harbor?.workers;
+ delete state.answers.training.harbor?.python;
+ }
+}
+function newAnswers(preset = "embedded-local") {
+ return { answers_version: 1, preset, training: {}, deployment: {} };
+}
+function field(path, label, type = "text", options = {}) {
+ const value = read(path),
+ id = path.replaceAll(".", "-");
+ let control;
+ if (options.choices)
+ control = `${options.choices.map((item) => `${item} `).join("")} `;
+ else if (type === "checkbox")
+ control = ` `;
+ else
+ control = ` `;
+ return `
${type === "checkbox" ? control : ""}${label} ${type === "checkbox" ? "" : control}${options.hint ? `${escapeHtml(options.hint)} ` : ""}
`;
+}
+const section = (title, content) =>
+ ``;
+function renderForm() {
+ const preset = state.answers.preset,
+ service = preset !== "embedded-local",
+ sandbox = preset === "service-sandbox-local-runner";
+ let html = "";
+ if (state.step === 0) {
+ const presets = [
+ ["embedded-local", "Local", "One machine", "monitor"],
+ ["service-ssh", "SSH workers", "Distributed Docker", "network"],
+ ["service-sandbox-local-runner", "Sandbox", "Controller + GPU", "box"],
+ ];
+ html = `
Deployment topology ${service ? "Service" : "Embedded"} ${presets.map(([id, label, detail, ico]) => `
${icon(ico)}${label}${detail}
`).join("")}
`;
+ html += section(
+ "Controller",
+ field("deployment.manager.source_dir", "Source directory", "text", {
+ full: true,
+ hint: "Path on the controller",
+ required: true,
+ }) +
+ field(
+ "deployment.manager.controller_python",
+ "Controller Python",
+ "text",
+ { full: true, required: true },
+ ) +
+ (service
+ ? field("deployment.manager.control_url", "Control origin", "url", {
+ placeholder: "http://controller.example:49200",
+ required: true,
+ }) +
+ field("deployment.manager.gateway_url", "Gateway origin", "url", {
+ placeholder: "http://controller.example:49184",
+ required: true,
+ })
+ : ""),
+ );
+ html += section(
+ "GPU target",
+ (service
+ ? field("deployment.gpu_target.ssh.host", "SSH destination", "text", {
+ placeholder: "gpu-training",
+ full: true,
+ required: true,
+ })
+ : "") +
+ field("deployment.gpu_target.source_dir", "Source directory", "text", {
+ full: true,
+ required: true,
+ hint: "Path on the GPU machine",
+ }) +
+ field("deployment.gpu_target.train_python", "Training Python", "text", {
+ full: true,
+ required: true,
+ }),
+ );
+ if (preset === "service-ssh")
+ html += ``;
+ else
+ html += section(
+ "Rollout runner",
+ field("training.harbor.python", "Harbor runner Python", "text", {
+ full: true,
+ required: true,
+ }) +
+ (sandbox
+ ? field("sandbox_config", "Sandbox configuration", "text", {
+ full: true,
+ placeholder: "/srv/site/sandbox.yaml",
+ required: true,
+ })
+ : ""),
+ );
+ html += section(
+ "Storage",
+ field(
+ "deployment.storage.run_root",
+ service ? "Shared run root" : "Run root",
+ "text",
+ {
+ full: true,
+ required: true,
+ hint: service
+ ? "Same absolute path on the controller, GPU and workers"
+ : "Path on this machine",
+ },
+ ),
+ );
+ } else if (state.step === 1) {
+ html += section(
+ "Model",
+ field("training.model.args_file", "Model args preset", "text", {
+ required: true,
+ }) +
+ field("training.harness.name", "Harness", "text", {
+ choices: state.bootstrap.catalog.harnesses.map((x) => x.id),
+ }) +
+ field("training.model.checkpoint", "Checkpoint directory", "text", {
+ full: true,
+ required: true,
+ hint: "Path on the GPU target",
+ }) +
+ field("training.model.reference", "Reference checkpoint", "text", {
+ full: true,
+ required: true,
+ }),
+ );
+ html += section(
+ "Tasks & agent",
+ field("training.tasks.catalog", "Task catalog", "text", {
+ full: true,
+ required: true,
+ placeholder: "/srv/inputs/tasks.jsonl",
+ hint: "An existing locked JSON / JSONL catalog on the controller",
+ }) +
+ field("training.harness.profile", "Claude Code profile", "text", {
+ full: true,
+ required: true,
+ placeholder: "/srv/inputs/claude-code.json",
+ }),
+ );
+ html += section(
+ "Gateway fingerprints",
+ field("training.gateway.tokenizer_digest", "Tokenizer SHA-256", "text", {
+ full: true,
+ required: true,
+ }) +
+ field("training.gateway.template_digest", "Template SHA-256", "text", {
+ full: true,
+ required: true,
+ }) +
+ (!service
+ ? field("training.gateway.host", "Bind address") +
+ field("training.gateway.advertised_url", "Advertised origin", "url")
+ : ""),
+ );
+ } else if (state.step === 2) {
+ html += section(
+ "GPU allocation",
+ field("training.deployment.layout", "Layout", "text", {
+ choices: ["colocate", "split"],
+ }) +
+ field("training.deployment.num_gpus", "Total GPUs", "number") +
+ field("training.deployment.actor_gpus", "Actor GPUs", "number") +
+ field("training.deployment.rollout_gpus", "Serving GPUs", "number") +
+ field(
+ "training.deployment.actor_tensor_parallel_size",
+ "Actor tensor parallelism",
+ "number",
+ ) +
+ field(
+ "training.deployment.rollout_gpus_per_engine",
+ "Serving tensor parallelism",
+ "number",
+ ),
+ );
+ html += section(
+ "Optimization",
+ field("training.training.num_rollout", "Rollout batches", "number") +
+ field("training.training.learning_rate", "Learning rate", "text") +
+ field("training.training.save_interval", "Save interval", "number") +
+ field(
+ "training.training.backend_contract",
+ "Algorithm contract",
+ "text",
+ {
+ choices: [
+ ...new Set(
+ state.bootstrap.catalog.algorithms.flatMap(
+ (x) => x.training_backend_contracts,
+ ),
+ ),
+ ],
+ },
+ ),
+ );
+ html += section(
+ "Sampling",
+ field("training.sampling.group_size", "Samples per group", "number") +
+ field(
+ "training.sampling.groups_per_batch",
+ "Groups per batch",
+ "number",
+ ) +
+ field(
+ "training.sampling.max_tokens",
+ "Maximum output tokens",
+ "number",
+ ) +
+ field("training.sampling.max_context", "Context limit", "number") +
+ field("training.sampling.max_attempts", "Maximum attempts", "number") +
+ field(
+ "training.gateway.audited_raw_logprobs",
+ "Audited raw log probabilities",
+ "checkbox",
+ ),
+ );
+ }
+ $("#form-sections").innerHTML = html;
+ $("#form-sections").hidden = state.step === 3;
+ $("#review").hidden = state.step !== 3;
+ if (state.step === 0 && preset === "service-ssh") renderWorkers();
+ $$("[data-path]").forEach((input) =>
+ input.addEventListener("change", () => {
+ const path = input.dataset.path;
+ let value =
+ input.type === "checkbox"
+ ? input.checked
+ : input.type === "number" || path.endsWith("learning_rate")
+ ? input.value === ""
+ ? null
+ : Number(input.value)
+ : input.value;
+ setAt(state.answers, path, value);
+ // Loaded bundles contain both sides of repeated schema fields.
+ const mirrors = {
+ "deployment.manager.control_url": null,
+ "deployment.manager.gateway_url": "training.gateway.advertised_url",
+ "deployment.storage.run_root": "training.output.root",
+ "training.gateway.host": "deployment.manager.bind",
+ "training.gateway.advertised_url": "deployment.manager.gateway_url",
+ };
+ if (mirrors[path] && valueAt(state.answers, mirrors[path]) !== undefined)
+ setAt(state.answers, mirrors[path], value);
+ if (
+ path === "sandbox_config" &&
+ valueAt(state.answers, "training.harbor.environment.config") !==
+ undefined
+ )
+ setAt(state.answers, "training.harbor.environment.config", value);
+ if (
+ path.startsWith("deployment.rollout.workers.") &&
+ state.answers.preset === "service-ssh"
+ ) {
+ deriveWorkers();
+ }
+ if (path === "training.deployment.num_gpus") {
+ if (
+ valueAt(state.answers, "deployment.gpu_target.expected_gpus") !==
+ undefined
+ )
+ setAt(state.answers, "deployment.gpu_target.expected_gpus", value);
+ if (read("training.deployment.layout") === "colocate")
+ ["actor_gpus", "rollout_gpus"].forEach((key) =>
+ setAt(state.answers, `training.deployment.${key}`, value),
+ );
+ }
+ if (path === "training.deployment.layout" && value === "colocate") {
+ ["actor_gpus", "rollout_gpus"].forEach((key) =>
+ setAt(
+ state.answers,
+ `training.deployment.${key}`,
+ read("training.deployment.num_gpus"),
+ ),
+ );
+ }
+ changed();
+ if (path.startsWith("training.deployment.")) renderForm();
+ }),
+ );
+ $$("[data-preset]").forEach((button) =>
+ button.addEventListener("click", () =>
+ action(async () => {
+ if (button.dataset.preset === state.answers.preset) return;
+ if (
+ !(await confirmAction(
+ "Change deployment?",
+ "This starts a new draft for the selected topology. Saved configurations remain available.",
+ "Change topology",
+ ))
+ )
+ return;
+ state.answers = newAnswers(button.dataset.preset);
+ changed();
+ renderForm();
+ }),
+ ),
+ );
+ $("#add-worker")?.addEventListener("click", () => {
+ const workers = state.answers.deployment.rollout?.workers || [];
+ workers.push({
+ id: `worker-${Date.now().toString(36)}`,
+ ssh: { host: "" },
+ source_dir: "",
+ runner_python: "",
+ docker: true,
+ task_roots: [""],
+ });
+ setAt(state.answers, "deployment.rollout.workers", workers);
+ deriveWorkers();
+ changed();
+ renderForm();
+ });
+ renderSummary();
+ updateActions();
+ icons();
+}
+function renderWorkers() {
+ const workers = state.answers.deployment.rollout?.workers || [];
+ $("#worker-fields").innerHTML = workers.length
+ ? workers
+ .map(
+ (worker, index) =>
+ `
Worker ${index + 1} ${icon("trash-2")} ${field(`deployment.rollout.workers.${index}.id`, "ID")}${field(`deployment.rollout.workers.${index}.ssh.host`, "SSH destination")}${field(`deployment.rollout.workers.${index}.source_dir`, "Source directory", "text", { full: true })}${field(`deployment.rollout.workers.${index}.runner_python`, "Runner Python", "text", { full: true })}${field(`deployment.rollout.workers.${index}.task_roots.0`, "Task root", "text", { full: true })}
`,
+ )
+ .join("")
+ : 'No workers configured.
';
+ $$("[data-remove-worker]").forEach((button) =>
+ button.addEventListener("click", () => {
+ workers.splice(Number(button.dataset.removeWorker), 1);
+ deriveWorkers();
+ changed();
+ renderForm();
+ }),
+ );
+}
+function renderSummary() {
+ const service = state.answers.preset !== "embedded-local",
+ sandbox = state.answers.preset === "service-sandbox-local-runner";
+ const nodes = [
+ ["monitor", "Controller", service ? "Service mode" : "Embedded mode"],
+ [
+ "cpu",
+ "GPU target",
+ service
+ ? read("deployment.gpu_target.ssh.host") || "Not configured"
+ : "Local machine",
+ ],
+ [
+ sandbox ? "box" : "network",
+ "Rollout",
+ sandbox
+ ? "Sandbox environment"
+ : service
+ ? `${state.answers.deployment.rollout?.workers?.length || 0} SSH workers`
+ : "Local Docker",
+ ],
+ ];
+ $("#topology").innerHTML = nodes
+ .map(
+ ([ico, title, detail], index) =>
+ `${index ? '
' : ""}${icon(ico)}
${title} ${escapeHtml(detail)}
`,
+ )
+ .join("");
+ const rows = [
+ ["Model", read("training.model.args_file")],
+ ["Algorithm", "GRPO"],
+ ["GPUs", read("training.deployment.num_gpus")],
+ ["Layout", read("training.deployment.layout")],
+ [
+ "Samples / batch",
+ Number(read("training.sampling.group_size")) *
+ Number(read("training.sampling.groups_per_batch")),
+ ],
+ ["Rollout batches", read("training.training.num_rollout")],
+ ];
+ $("#resource-summary").innerHTML =
+ `${rows.map(([key, value]) => `${key} ${escapeHtml(value)} `).join("")} `;
+ $("#summary-status").textContent = state.saved
+ ? "Saved configuration"
+ : "Unsaved configuration";
+ icons();
+}
+function updateActions() {
+ $("#previous").disabled = state.step === 0 || state.busy;
+ $("#next").hidden = state.step === 3;
+ $("#save").hidden = state.step !== 3;
+ $("#next").disabled = state.busy;
+ $("#save").disabled =
+ state.busy ||
+ !state.preview ||
+ state.preview.validation.status === "invalid" ||
+ !!state.saved;
+ $("#step-description").textContent = `Step ${state.step + 1} of 4`;
+ $$("[data-step]").forEach((button) => {
+ button.classList.toggle(
+ "active",
+ Number(button.dataset.step) === state.step,
+ );
+ button.setAttribute(
+ "aria-current",
+ Number(button.dataset.step) === state.step ? "step" : "false",
+ );
+ });
+ const active = state.jobs.some((job) =>
+ ["running", "stopping", "unknown"].includes(job.state),
+ );
+ $("#dry-run").disabled = !state.saved || state.busy || active;
+ $("#doctor").disabled =
+ !state.saved || state.busy || active || !state.bootstrap?.live_supported;
+ $("#train").disabled =
+ !state.saved || state.busy || active || !state.bootstrap?.live_supported;
+}
+async function preview(checkFiles = false) {
+ state.preview = null;
+ $("#validation-result").textContent = "Checking configuration...";
+ $("#config-preview").textContent = "";
+ try {
+ state.preview = await api("/api/preview", {
+ name: $("#config-name").value,
+ answers: state.answers,
+ check_files: checkFiles,
+ });
+ const result = state.preview.validation;
+ $("#validation-result").innerHTML =
+ `${result.status === "invalid" ? "Changes required" : "Structure valid · runtime checks deferred"}
${result.checks.map((row) => `${icon(row.status === "passed" ? "circle-check" : row.status === "failed" ? "circle-x" : "clock-3")}
${escapeHtml(row.path)} ${escapeHtml(row.message)}
`).join("")}`;
+ renderPreview();
+ } catch (error) {
+ $("#validation-result").textContent = error.message;
+ throw error;
+ } finally {
+ updateActions();
+ icons();
+ }
+}
+function renderPreview() {
+ $("#config-preview").textContent = state.preview?.files[state.file] || "";
+ $$("[data-file]").forEach((button) =>
+ button.classList.toggle("active", button.dataset.file === state.file),
+ );
+}
+async function goStep(step) {
+ state.step = step;
+ renderForm();
+ if (step === 3) await preview();
+}
+function showView(view) {
+ state.view = view;
+ $("#configuration-view").hidden = view !== "configuration";
+ $("#runs-view").hidden = view !== "runs";
+ $$("[data-view]").forEach((button) =>
+ button.classList.toggle("active", button.dataset.view === view),
+ );
+ if (view === "runs") renderJobs();
+}
+function renderSaved() {
+ const configurations = state.bootstrap.configurations;
+ $("#saved-list").innerHTML = configurations.length
+ ? configurations
+ .map(
+ (config) =>
+ `${icon("file-code-2")}${escapeHtml(config.id)} `,
+ )
+ .join("")
+ : 'No configurations yet ';
+ $$("[data-config]").forEach((button) =>
+ button.addEventListener("click", () =>
+ action(async () => {
+ if (
+ !state.saved &&
+ !(await confirmAction(
+ "Open saved configuration?",
+ "The current draft will be replaced.",
+ "Open configuration",
+ ))
+ )
+ return;
+ const config = await api(
+ `/api/configurations/${encodeURIComponent(button.dataset.config)}`,
+ );
+ state.answers = config.answers;
+ state.saved = config.id;
+ $("#config-name").value = config.id;
+ $("#draft-state").textContent = "Saved";
+ $("#draft-state").className = "badge saved";
+ showView("configuration");
+ await goStep(3);
+ renderSaved();
+ }),
+ ),
+ );
+ icons();
+}
+function download(name, text, type = "text/plain") {
+ const url = URL.createObjectURL(new Blob([text], { type }));
+ const anchor = document.createElement("a");
+ anchor.href = url;
+ anchor.download = name;
+ anchor.click();
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
+}
+function confirmAction(title, message, label = "Confirm") {
+ const dialog = $("#confirm-dialog");
+ $("#confirm-title").textContent = title;
+ $("#confirm-message").textContent = message;
+ $("#confirm-action").textContent = label;
+ dialog.returnValue = "cancel";
+ return new Promise((resolve) => {
+ dialog.addEventListener(
+ "close",
+ () => resolve(dialog.returnValue === "confirm"),
+ { once: true },
+ );
+ dialog.showModal();
+ });
+}
+async function startJob(mode) {
+ if (
+ mode !== "dry-run" &&
+ !(await confirmAction(
+ mode === "train" ? "Start training?" : "Run live environment checks?",
+ mode === "train"
+ ? `Configuration: ${state.saved}\nGPU target: ${read("deployment.gpu_target.ssh.host") || "Local machine"}\nGPU budget: ${read("training.deployment.num_gpus")}\n\nTraining runs preflight, then allocates the configured GPU and rollout resources.`
+ : "Doctor executes environment and network checks on the configured machines.",
+ mode === "train" ? "Start training" : "Run doctor",
+ ))
+ )
+ return;
+ const job = await api("/api/jobs", {
+ configuration: state.saved,
+ mode,
+ confirmed: mode !== "dry-run",
+ });
+ state.selectedJob = job.id;
+ showView("runs");
+ await refreshJobs();
+}
+function renderJobs() {
+ $("#run-count").textContent = state.jobs.length;
+ $("#jobs-list").innerHTML = state.jobs.length
+ ? `CONFIGURATION MODE STARTED STATUS EXIT ${state.jobs.map((job) => `${escapeHtml(job.configuration)}
${job.id.slice(0, 8)}
${escapeHtml(job.mode)} ${new Date(job.created_at).toLocaleString()} ${escapeHtml(job.state)} ${job.exit_code ?? "—"} `).join("")}
`
+ : `${icon("activity")}
No runs yet Your dry-runs, environment checks and training jobs appear here.
`;
+ $$("[data-job]").forEach((row) => {
+ const select = () =>
+ action(async () => {
+ state.selectedJob = row.dataset.job;
+ renderJobs();
+ await loadLog();
+ });
+ row.addEventListener("click", select);
+ row.addEventListener("keydown", (event) => {
+ if (event.key === "Enter") select();
+ });
+ });
+ icons();
+}
+async function loadLog() {
+ if (!state.selectedJob) return;
+ const result = await api(`/api/jobs/${state.selectedJob}`),
+ job = result.job;
+ $("#job-detail").hidden = false;
+ $("#log-selector").hidden = false;
+ $("#job-title").textContent = `${job.configuration} · ${job.mode}`;
+ $("#job-meta").textContent =
+ `${job.state} · ${job.snapshot}${job.message ? ` · ${job.message}` : ""}`;
+ const log = $("#job-log"),
+ atBottom = log.scrollTop + log.clientHeight >= log.scrollHeight - 25;
+ const training = state.logSource === "training";
+ log.textContent =
+ (training ? result.training_text : result.text) ||
+ "Waiting for process output...";
+ if (atBottom) log.scrollTop = log.scrollHeight;
+ $("#log-note").textContent =
+ `${(training ? result.training_truncated : result.truncated) ? "Showing the last 64 KiB. " : ""}${training ? result.training_log_path || "No separate GPU log yet" : job.log_path} · Output: ${job.output_root}`;
+ $("#stop-job").hidden = !["running", "stopping"].includes(job.state);
+ $("#stop-job").disabled = job.state === "stopping";
+ $("#reconcile-job").hidden = job.state !== "unknown";
+}
+async function refreshJobs() {
+ state.jobs = await api("/api/jobs");
+ renderJobs();
+ updateActions();
+ if (state.view === "runs") await loadLog();
+}
+async function importAnswers(text) {
+ const { answers } = await api("/api/import", { text });
+ state.answers = answers;
+ changed();
+ state.step = 0;
+ renderForm();
+ showView("configuration");
+ notify("Answers imported", true);
+}
+async function initialize() {
+ icons();
+ try {
+ state.bootstrap = await api("/api/bootstrap");
+ state.jobs = state.bootstrap.jobs;
+ state.answers = newAnswers();
+ $("#workspace-path").textContent = state.bootstrap.workspace;
+ $("#connection-text").textContent = "Local session";
+ renderForm();
+ renderSaved();
+ renderJobs();
+ if (!state.bootstrap.live_supported)
+ notify(
+ "Configuration and dry-run are available here. Start this console on the Linux controller for live training.",
+ );
+ } catch (error) {
+ $("#connection-text").textContent = "Disconnected";
+ $(".connection").classList.add("offline");
+ notify(error.message);
+ return;
+ }
+ $$("[data-view]").forEach((button) =>
+ button.addEventListener("click", () => showView(button.dataset.view)),
+ );
+ $$("[data-step]").forEach((button) =>
+ button.addEventListener("click", () =>
+ action(() => goStep(Number(button.dataset.step))),
+ ),
+ );
+ $("#previous").addEventListener("click", () =>
+ action(() => goStep(state.step - 1)),
+ );
+ $("#next").addEventListener("click", () =>
+ action(() => goStep(state.step + 1)),
+ );
+ $("#config-name").addEventListener("change", () => {
+ changed();
+ if (state.step === 3) action(() => preview());
+ else renderForm();
+ });
+ $("#check-files").addEventListener("click", () =>
+ action(() => preview(true)),
+ );
+ $$("[data-file]").forEach((button) =>
+ button.addEventListener("click", () => {
+ state.file = button.dataset.file;
+ renderPreview();
+ }),
+ );
+ $("#download-file").addEventListener("click", () => {
+ if (state.preview) download(state.file, state.preview.files[state.file]);
+ });
+ $("#save").addEventListener("click", () =>
+ action(async () => {
+ const saved = await api("/api/configurations", {
+ name: $("#config-name").value,
+ answers: state.answers,
+ });
+ state.saved = saved.id;
+ state.bootstrap.configurations.push(saved);
+ $("#draft-state").textContent = "Saved";
+ $("#draft-state").className = "badge saved";
+ renderSaved();
+ renderSummary();
+ notify(`Saved ${saved.id}`, true);
+ }),
+ );
+ ["dry-run", "doctor", "train"].forEach((mode) =>
+ $("#" + mode).addEventListener("click", () => action(() => startJob(mode))),
+ );
+ $("#new-config").addEventListener("click", () =>
+ action(async () => {
+ if (
+ !(await confirmAction(
+ "New configuration?",
+ "Start a fresh draft?",
+ "New configuration",
+ ))
+ )
+ return;
+ state.answers = newAnswers();
+ $("#config-name").value =
+ `experiment-${state.bootstrap.configurations.length + 1}`;
+ state.step = 0;
+ changed();
+ showView("configuration");
+ renderForm();
+ renderSaved();
+ }),
+ );
+ $("#answers-button").addEventListener("click", () => {
+ $("#answers-error").hidden = true;
+ $("#answers-editor").value = JSON.stringify(state.answers, null, 2);
+ $("#answers-dialog").showModal();
+ });
+ $("#close-answers").addEventListener("click", () =>
+ $("#answers-dialog").close(),
+ );
+ $("#apply-answers").addEventListener("click", () =>
+ action(async () => {
+ try {
+ await importAnswers($("#answers-editor").value);
+ $("#answers-dialog").close();
+ } catch (error) {
+ $("#answers-error").textContent = error.message;
+ $("#answers-error").hidden = false;
+ }
+ }),
+ );
+ $("#mobile-new").addEventListener("click", () => $("#new-config").click());
+ $("#export-answers").addEventListener("click", () =>
+ download("answers.json", $("#answers-editor").value, "application/json"),
+ );
+ $("#import-button").addEventListener("click", () =>
+ $("#import-file").click(),
+ );
+ $("#import-file").addEventListener("change", () =>
+ action(async () => {
+ const file = $("#import-file").files[0];
+ if (file) {
+ if (file.size > 1048576)
+ throw new Error("Answers file must be at most 1 MiB");
+ await importAnswers(await file.text());
+ }
+ $("#import-file").value = "";
+ }),
+ );
+ $("#refresh-jobs").addEventListener("click", () => action(refreshJobs));
+ $("#stop-job").addEventListener("click", () =>
+ action(async () => {
+ if (
+ await confirmAction(
+ "Stop this job?",
+ "Send a graceful stop request through the existing training CLI?",
+ "Stop job",
+ )
+ ) {
+ await api("/api/stop", { id: state.selectedJob });
+ await refreshJobs();
+ }
+ }),
+ );
+ $("#reconcile-job").addEventListener("click", () =>
+ action(async () => {
+ if (
+ await confirmAction(
+ "Confirm target cleanup?",
+ "Only continue after checking that the previous GPU and Sandbox processes have stopped. This releases the interrupted-job block.",
+ "Cleanup confirmed",
+ )
+ ) {
+ await api("/api/reconcile", { id: state.selectedJob, confirmed: true });
+ await refreshJobs();
+ }
+ }),
+ );
+ $("#copy-log").addEventListener("click", () =>
+ download(`${state.selectedJob}.log`, $("#job-log").textContent),
+ );
+ $("#log-source").addEventListener("change", () => {
+ state.logSource = $("#log-source").value;
+ action(loadLog);
+ });
+ setInterval(async () => {
+ if (state.busy) return;
+ try {
+ await refreshJobs();
+ $("#connection-text").textContent = "Local session";
+ $(".connection").classList.remove("offline");
+ } catch {
+ $("#connection-text").textContent = "Disconnected";
+ $(".connection").classList.add("offline");
+ }
+ }, 2500);
+}
+initialize();
diff --git a/harborrl/webui/static/brand.jpg b/harborrl/webui/static/brand.jpg
new file mode 100644
index 000000000..e7cd9b29f
Binary files /dev/null and b/harborrl/webui/static/brand.jpg differ
diff --git a/harborrl/webui/static/index.html b/harborrl/webui/static/index.html
new file mode 100644
index 000000000..bc76f9f58
--- /dev/null
+++ b/harborrl/webui/static/index.html
@@ -0,0 +1,273 @@
+
+
+
+
+
+ HarborRL · Training console
+
+
+
+
+
+
+
+
+ Training console
+
+ Connecting
+
+
+
+
+
+
+
+
+
+
EXPERIMENT SETUP
+
Training configuration
+
+
+
+
+
+
+ Import answers
+
+
+
+
+ Configuration name Draft
+
+
+
+ 1 Deployment 2 Model & tasks3 Training4 Review & launch
+
+
+
+
+
+
+
Configuration checks
+
+ Check local files
+
+
+
+ No checks run
+
+
+
Generated files
+
+
+
+
+
+
+ config.yaml deployment.yaml manifest
+
+
+Complete required fields to preview.
+
+
+
Launch
+
+ Live runs use the controller environment and configured
+ GPU target.
+
+
+
+
+ Dry-run
+ Doctor
+ Start training
+
+
+
+
+
+
+
+
+
+
Execution plan
+
+
+
+
+
+
+
+
+
+
+
+ Log source
+ Controller
+ GPU training
+
+
+
+
+
+
+
+
+ Confirm cleanup
+ Stop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Advanced answers
+
+
+
+
+
+
+
+
+ Export answers Apply answers
+
+
+
+
diff --git a/harborrl/webui/static/lucide-LICENSE.txt b/harborrl/webui/static/lucide-LICENSE.txt
new file mode 100644
index 000000000..718bb3f0e
--- /dev/null
+++ b/harborrl/webui/static/lucide-LICENSE.txt
@@ -0,0 +1,43 @@
+ISC License
+
+Copyright (c) 2026 Lucide Icons and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+---
+
+The following Lucide icons are derived from the Feather project:
+
+airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
+
+The MIT License (MIT) (for the icons listed above)
+
+Copyright (c) 2013-present Cole Bemis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/harborrl/webui/static/lucide.min.js b/harborrl/webui/static/lucide.min.js
new file mode 100644
index 000000000..e3e5586e9
--- /dev/null
+++ b/harborrl/webui/static/lucide.min.js
@@ -0,0 +1,12 @@
+/**
+ * @license lucide v1.61.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */
+
+(function(a,i){typeof exports=="object"&&typeof module<"u"?i(exports):typeof define=="function"&&define.amd?define(["exports"],i):(a=typeof globalThis<"u"?globalThis:a||self,i(a.lucide={}))})(this,(function(a){"use strict";const i={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"},Ka=([h,t,c])=>{const d=document.createElementNS("http://www.w3.org/2000/svg",h);return Object.keys(t).forEach(M=>{d.setAttribute(M,String(t[M]))}),c?.length&&c.forEach(M=>{const p=Ka(M);d.appendChild(p)}),d},Ja=(h,t={})=>{const c={...i,...t};return Ka(["svg",c,h])},FS=(...h)=>h.filter((t,c,d)=>!!t&&t.trim()!==""&&d.indexOf(t)===c).join(" ").trim(),RS=h=>{for(const t in h)if(t.startsWith("aria-")||t==="role"||t==="title")return!0;return!1},bS=h=>{let t="",c=!1;for(const d of h){if(d==="-"||d==="_"||d<=" "){c=t.length>0;continue}t.length===0?t+=d.toLowerCase():t+=c?d.toUpperCase():d,c=!1}return t},TS=h=>{const t=bS(h);return t.charAt(0).toUpperCase()+t.slice(1)},qS=h=>Array.from(h.attributes).reduce((t,c)=>(t[c.name]=c.value,t),{}),Qa=h=>typeof h=="string"?h:!h||!h.class?"":h.class&&typeof h.class=="string"?h.class.split(" "):h.class&&Array.isArray(h.class)?h.class:"",ja=(h,{nameAttr:t,icons:c,attrs:d})=>{const M=h.getAttribute(t);if(M==null)return;const p=TS(M),Xa=c[p];if(!Xa)return console.warn(`${h.outerHTML} icon name was not found in the provided icons object.`);const Na=qS(h),US=RS(Na)?{}:{"aria-hidden":"true"},zS={...i,"data-lucide":M,...US,...d,...Na},OS=Qa(Na),GS=Qa(d),DS=FS("lucide",`lucide-${M}`,...OS,...GS);DS&&Object.assign(zS,{class:DS});const ZS=Ja(Xa,zS);return h.parentNode?.replaceChild(ZS,h)},Ya=[["path",{d:"m14 12 4 4 4-4"}],["path",{d:"M18 16V7"}],["path",{d:"m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16"}],["path",{d:"M3.304 13h6.392"}]],_a=[["path",{d:"m14 11 4-4 4 4"}],["path",{d:"M18 16V7"}],["path",{d:"m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16"}],["path",{d:"M3.304 13h6.392"}]],xa=[["path",{d:"m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16"}],["path",{d:"M15.697 14h5.606"}],["path",{d:"m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16"}],["path",{d:"M3.304 13h6.392"}]],ah=[["circle",{cx:"16",cy:"4",r:"1"}],["path",{d:"m18 19 1-7-6 1"}],["path",{d:"m5 8 3-3 5.5 3-2.36 3.5"}],["path",{d:"M4.24 14.5a5 5 0 0 0 6.88 6"}],["path",{d:"M13.76 17.5a5 5 0 0 0-6.88-6"}]],hh=[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"}]],th=[["path",{d:"M10 13H6"}],["path",{d:"M10 15v-4a2 2 0 0 0-4 0v4"}],["path",{d:"M14 14.5a.5.5 0 0 0 .5.5h1a2.5 2.5 0 0 0 2.5-2.5v-1A2.5 2.5 0 0 0 15.5 9h-1a.5.5 0 0 0-.5.5z"}],["rect",{x:"2",y:"5",width:"20",height:"14",rx:"2"}]],dh=[["path",{d:"M18 17.5a2.5 2.5 0 1 1-4 2.03V12"}],["path",{d:"M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"}],["path",{d:"M6 8h12"}],["path",{d:"M6.6 15.572A2 2 0 1 0 10 17v-5"}]],ch=[["path",{d:"M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"}],["path",{d:"m12 15 5 6H7Z"}]],C=[["circle",{cx:"12",cy:"13",r:"8"}],["path",{d:"M5 3 2 6"}],["path",{d:"m22 6-3-3"}],["path",{d:"M6.38 18.7 4 21"}],["path",{d:"M17.64 18.67 20 21"}],["path",{d:"m9 13 2 2 4-4"}]],u=[["circle",{cx:"12",cy:"13",r:"8"}],["path",{d:"M5 3 2 6"}],["path",{d:"m22 6-3-3"}],["path",{d:"M6.38 18.7 4 21"}],["path",{d:"M17.64 18.67 20 21"}],["path",{d:"M9 13h6"}]],Mh=[["path",{d:"M6.87 6.87a8 8 0 1 0 11.26 11.26"}],["path",{d:"M19.9 14.25a8 8 0 0 0-9.15-9.15"}],["path",{d:"m22 6-3-3"}],["path",{d:"M6.26 18.67 4 21"}],["path",{d:"m2 2 20 20"}],["path",{d:"M4 4 2 6"}]],g=[["circle",{cx:"12",cy:"13",r:"8"}],["path",{d:"M5 3 2 6"}],["path",{d:"m22 6-3-3"}],["path",{d:"M6.38 18.7 4 21"}],["path",{d:"M17.64 18.67 20 21"}],["path",{d:"M12 10v6"}],["path",{d:"M9 13h6"}]],ph=[["circle",{cx:"12",cy:"13",r:"8"}],["path",{d:"M12 9v4l2 2"}],["path",{d:"M5 3 2 6"}],["path",{d:"m22 6-3-3"}],["path",{d:"M6.38 18.7 4 21"}],["path",{d:"M17.64 18.67 20 21"}]],lh=[["path",{d:"M11 21c0-2.5 2-2.5 2-5"}],["path",{d:"M16 21c0-2.5 2-2.5 2-5"}],["path",{d:"m19 8-.8 3a1.25 1.25 0 0 1-1.2 1H7a1.25 1.25 0 0 1-1.2-1L5 8"}],["path",{d:"M21 3a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a1 1 0 0 1 1-1z"}],["path",{d:"M6 21c0-2.5 2-2.5 2-5"}]],ih=[["path",{d:"M2 12h20"}],["path",{d:"M10 16v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-4"}],["path",{d:"M10 8V4a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v4"}],["path",{d:"M20 16v1a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-1"}],["path",{d:"M14 8V7c0-1.1.9-2 2-2h2a2 2 0 0 1 2 2v1"}]],nh=[["path",{d:"M12 2v20"}],["path",{d:"M8 10H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h4"}],["path",{d:"M16 10h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-4"}],["path",{d:"M8 20H7a2 2 0 0 1-2-2v-2c0-1.1.9-2 2-2h1"}],["path",{d:"M16 14h1a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-1"}]],eh=[["rect",{width:"6",height:"16",x:"4",y:"2",rx:"2"}],["rect",{width:"6",height:"9",x:"14",y:"9",rx:"2"}],["path",{d:"M22 22H2"}]],rh=[["rect",{width:"16",height:"6",x:"2",y:"4",rx:"2"}],["rect",{width:"9",height:"6",x:"9",y:"14",rx:"2"}],["path",{d:"M22 22V2"}]],oh=[["rect",{width:"6",height:"14",x:"4",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"14",y:"7",rx:"2"}],["path",{d:"M17 22v-5"}],["path",{d:"M17 7V2"}],["path",{d:"M7 22v-3"}],["path",{d:"M7 5V2"}]],vh=[["rect",{width:"6",height:"14",x:"4",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"14",y:"7",rx:"2"}],["path",{d:"M10 2v20"}],["path",{d:"M20 2v20"}]],$h=[["rect",{width:"6",height:"14",x:"4",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"14",y:"7",rx:"2"}],["path",{d:"M4 2v20"}],["path",{d:"M14 2v20"}]],mh=[["rect",{width:"6",height:"14",x:"2",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"16",y:"7",rx:"2"}],["path",{d:"M12 2v20"}]],yh=[["rect",{width:"6",height:"14",x:"2",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"12",y:"7",rx:"2"}],["path",{d:"M22 2v20"}]],sh=[["rect",{width:"6",height:"14",x:"6",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"16",y:"7",rx:"2"}],["path",{d:"M2 2v20"}]],Ch=[["rect",{width:"6",height:"10",x:"9",y:"7",rx:"2"}],["path",{d:"M4 22V2"}],["path",{d:"M20 22V2"}]],uh=[["rect",{width:"6",height:"14",x:"3",y:"5",rx:"2"}],["rect",{width:"6",height:"10",x:"15",y:"7",rx:"2"}],["path",{d:"M3 2v20"}],["path",{d:"M21 2v20"}]],gh=[["rect",{width:"6",height:"16",x:"4",y:"6",rx:"2"}],["rect",{width:"6",height:"9",x:"14",y:"6",rx:"2"}],["path",{d:"M22 2H2"}]],Ah=[["path",{d:"M22 17h-3"}],["path",{d:"M22 7h-5"}],["path",{d:"M5 17H2"}],["path",{d:"M7 7H2"}],["rect",{x:"5",y:"14",width:"14",height:"6",rx:"2"}],["rect",{x:"7",y:"4",width:"10",height:"6",rx:"2"}]],Hh=[["rect",{width:"9",height:"6",x:"6",y:"14",rx:"2"}],["rect",{width:"16",height:"6",x:"6",y:"4",rx:"2"}],["path",{d:"M2 2v20"}]],Vh=[["rect",{width:"14",height:"6",x:"5",y:"14",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"4",rx:"2"}],["path",{d:"M2 20h20"}],["path",{d:"M2 10h20"}]],Sh=[["rect",{width:"14",height:"6",x:"5",y:"14",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"4",rx:"2"}],["path",{d:"M2 14h20"}],["path",{d:"M2 4h20"}]],wh=[["rect",{width:"14",height:"6",x:"5",y:"16",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"2",rx:"2"}],["path",{d:"M2 12h20"}]],Lh=[["rect",{width:"14",height:"6",x:"5",y:"12",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"2",rx:"2"}],["path",{d:"M2 22h20"}]],fh=[["rect",{width:"10",height:"6",x:"7",y:"9",rx:"2"}],["path",{d:"M22 20H2"}],["path",{d:"M22 4H2"}]],kh=[["rect",{width:"14",height:"6",x:"5",y:"16",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"6",rx:"2"}],["path",{d:"M2 2h20"}]],Ph=[["rect",{width:"14",height:"6",x:"5",y:"15",rx:"2"}],["rect",{width:"10",height:"6",x:"7",y:"3",rx:"2"}],["path",{d:"M2 21h20"}],["path",{d:"M2 3h20"}]],Bh=[["path",{d:"M10 10H6"}],["path",{d:"M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2"}],["path",{d:"M19 18h2a1 1 0 0 0 1-1v-3.28a1 1 0 0 0-.684-.948l-1.923-.641a1 1 0 0 1-.578-.502l-1.539-3.076A1 1 0 0 0 16.382 8H14"}],["path",{d:"M8 8v4"}],["path",{d:"M9 18h6"}],["circle",{cx:"17",cy:"18",r:"2"}],["circle",{cx:"7",cy:"18",r:"2"}]],zh=[["path",{d:"M16 12h3"}],["path",{d:"M17.5 12a8 8 0 0 1-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13"}]],Dh=[["path",{d:"M10 17c-5-3-7-7-7-9a2 2 0 0 1 4 0c0 2.5-5 2.5-5 6 0 1.7 1.3 3 3 3 2.8 0 5-2.2 5-5"}],["path",{d:"M22 17c-5-3-7-7-7-9a2 2 0 0 1 4 0c0 2.5-5 2.5-5 6 0 1.7 1.3 3 3 3 2.8 0 5-2.2 5-5"}]],Fh=[["path",{d:"M10 2v5.632c0 .424-.272.795-.653.982A6 6 0 0 0 6 14c.006 4 3 7 5 8"}],["path",{d:"M10 5H8a2 2 0 0 0 0 4h.68"}],["path",{d:"M14 2v5.632c0 .424.272.795.652.982A6 6 0 0 1 18 14c0 4-3 7-5 8"}],["path",{d:"M14 5h2a2 2 0 0 1 0 4h-.68"}],["path",{d:"M18 22H6"}],["path",{d:"M9 2h6"}]],Rh=[["path",{d:"M12 6v16"}],["path",{d:"m19 13 2-1a9 9 0 0 1-18 0l2 1"}],["path",{d:"M9 11h6"}],["circle",{cx:"12",cy:"4",r:"2"}]],bh=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M3 11a10 10 0 0 1 10 10"}]],Th=[["path",{d:"M2 12 7 2"}],["path",{d:"m7 12 5-10"}],["path",{d:"m12 12 5-10"}],["path",{d:"m17 12 5-10"}],["path",{d:"M4.5 7h15"}],["path",{d:"M12 16v6"}]],qh=[["path",{d:"M7 10H6a4 4 0 0 1-4-4 1 1 0 0 1 1-1h4"}],["path",{d:"M7 5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1 7 7 0 0 1-7 7H8a1 1 0 0 1-1-1z"}],["path",{d:"M9 12v5"}],["path",{d:"M15 12v5"}],["path",{d:"M5 20a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3 1 1 0 0 1-1 1H6a1 1 0 0 1-1-1"}]],Uh=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m14.31 8 5.74 9.94"}],["path",{d:"M9.69 8h11.48"}],["path",{d:"m7.38 12 5.74-9.94"}],["path",{d:"M9.69 16 3.95 6.06"}],["path",{d:"M14.31 16H2.83"}],["path",{d:"m16.62 12-5.74 9.94"}]],Oh=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M6 8h.01"}],["path",{d:"M10 8h.01"}],["path",{d:"M14 8h.01"}]],Gh=[["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}],["path",{d:"M10 4v4"}],["path",{d:"M2 8h20"}],["path",{d:"M6 4v4"}]],Zh=[["path",{d:"M12 6.528V3a1 1 0 0 1 1-1h0"}],["path",{d:"M18.237 21A15 15 0 0 0 22 11a6 6 0 0 0-10-4.472A6 6 0 0 0 2 11a15.1 15.1 0 0 0 3.763 10 3 3 0 0 0 3.648.648 5.5 5.5 0 0 1 5.178 0A3 3 0 0 0 18.237 21"}]],Eh=[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h2"}],["path",{d:"M20 8v11a2 2 0 0 1-2 2h-2"}],["path",{d:"m9 15 3-3 3 3"}],["path",{d:"M12 12v9"}]],Wh=[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"}],["path",{d:"M10 12h4"}]],Ih=[["path",{d:"M19 9V6a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v3"}],["path",{d:"M3 16a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-5a2 2 0 0 0-4 0v1.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V11a2 2 0 0 0-4 0z"}],["path",{d:"M5 18v2"}],["path",{d:"M19 18v2"}]],Xh=[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"}],["path",{d:"m9.5 17 5-5"}],["path",{d:"m9.5 12 5 5"}]],Nh=[["path",{d:"M11 10h8"}],["path",{d:"M11 14h8"}],["path",{d:"M17 20V10a6 6 0 0 0-12 0"}]],Kh=[["path",{d:"M14 8a1 1 0 0 1 1 1v2a1 1 0 0 0 1 1h3.293a.707.707 0 0 1 .5 1.207l-6.939 6.939a1.207 1.207 0 0 1-1.708 0l-6.94-6.94a.707.707 0 0 1 .5-1.206H8a1 1 0 0 0 1-1V9a1 1 0 0 1 1-1z"}],["path",{d:"M9 4h6"}]],Jh=[["path",{d:"M9 5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v6a1 1 0 0 0 1 1h3.293a.707.707 0 0 1 .5 1.207l-7.086 7.086a1 1 0 0 1-1.414 0l-7.086-7.086a.707.707 0 0 1 .5-1.207H8a1 1 0 0 0 1-1z"}]],Qh=[["path",{d:"M13 9a1 1 0 0 1-1-1V4.707a.707.707 0 0 0-1.207-.5l-6.94 6.94a1.207 1.207 0 0 0 0 1.707l6.94 6.94a.707.707 0 0 0 1.207-.5V16a1 1 0 0 1 1-1h2a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1z"}],["path",{d:"M20 9v6"}]],jh=[["path",{d:"M10.793 19.793a.707.707 0 0 0 1.207-.5V16a1 1 0 0 1 1-1h6a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-6a1 1 0 0 1-1-1V4.707a.707.707 0 0 0-1.207-.5l-6.94 6.94a1.207 1.207 0 0 0 0 1.707z"}]],Yh=[["path",{d:"M11 9a1 1 0 0 0 1-1V4.707a.707.707 0 0 1 1.207-.5l6.94 6.94a1.207 1.207 0 0 1 0 1.707l-6.94 6.94a.707.707 0 0 1-1.207-.5V16a1 1 0 0 0-1-1H9a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1z"}],["path",{d:"M4 9v6"}]],_h=[["path",{d:"M13.207 19.793a.707.707 0 0 1-1.207-.5V16a1 1 0 0 0-1-1H5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h6a1 1 0 0 0 1-1V4.707a.707.707 0 0 1 1.207-.5l6.94 6.94a1.207 1.207 0 0 1 0 1.707z"}]],xh=[["path",{d:"M14 16a1 1 0 0 0 1-1v-2a1 1 0 0 1 1-1h3.293a.707.707 0 0 0 .5-1.207l-6.939-6.939a1.207 1.207 0 0 0-1.708 0l-6.94 6.94a.707.707 0 0 0 .5 1.206H8a1 1 0 0 1 1 1v2a1 1 0 0 0 1 1z"}],["path",{d:"M9 20h6"}]],at=[["path",{d:"M9 19a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-6a1 1 0 0 1 1-1h3.293a.707.707 0 0 0 .5-1.207l-7.086-7.086a1 1 0 0 0-1.414 0l-7.086 7.086a.707.707 0 0 0 .5 1.207H8a1 1 0 0 1 1 1z"}]],ht=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["rect",{x:"15",y:"4",width:"4",height:"6",ry:"2"}],["path",{d:"M17 20v-6h-2"}],["path",{d:"M15 20h4"}]],tt=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["path",{d:"M17 10V4h-2"}],["path",{d:"M15 10h4"}],["rect",{x:"15",y:"14",width:"4",height:"6",ry:"2"}]],A=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["path",{d:"M20 8h-5"}],["path",{d:"M15 10V6.5a2.5 2.5 0 0 1 5 0V10"}],["path",{d:"M15 14h5l-5 6h5"}]],dt=[["path",{d:"M19 3H5"}],["path",{d:"M12 21V7"}],["path",{d:"m6 15 6 6 6-6"}]],ct=[["path",{d:"M17 7 7 17"}],["path",{d:"M17 17H7V7"}]],Mt=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["path",{d:"M11 4h4"}],["path",{d:"M11 8h7"}],["path",{d:"M11 12h10"}]],pt=[["path",{d:"m7 7 10 10"}],["path",{d:"M17 7v10H7"}]],lt=[["path",{d:"M12 2v14"}],["path",{d:"m19 9-7 7-7-7"}],["circle",{cx:"12",cy:"21",r:"1"}]],it=[["path",{d:"M12 17V3"}],["path",{d:"m6 11 6 6 6-6"}],["path",{d:"M19 21H5"}]],nt=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["path",{d:"m21 8-4-4-4 4"}],["path",{d:"M17 4v16"}]],H=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 20V4"}],["path",{d:"M11 4h10"}],["path",{d:"M11 8h7"}],["path",{d:"M11 12h4"}]],V=[["path",{d:"m3 16 4 4 4-4"}],["path",{d:"M7 4v16"}],["path",{d:"M15 4h5l-5 6h5"}],["path",{d:"M15 20v-3.5a2.5 2.5 0 0 1 5 0V20"}],["path",{d:"M20 18h-5"}]],et=[["path",{d:"M12 5v14"}],["path",{d:"m19 12-7 7-7-7"}]],rt=[["path",{d:"m9 6-6 6 6 6"}],["path",{d:"M3 12h14"}],["path",{d:"M21 19V5"}]],ot=[["path",{d:"M3 19V5"}],["path",{d:"m13 6-6 6 6 6"}],["path",{d:"M7 12h14"}]],vt=[["path",{d:"M8 3 4 7l4 4"}],["path",{d:"M4 7h16"}],["path",{d:"m16 21 4-4-4-4"}],["path",{d:"M20 17H4"}]],$t=[["path",{d:"m12 19-7-7 7-7"}],["path",{d:"M19 12H5"}]],mt=[["path",{d:"M3 5v14"}],["path",{d:"M21 12H7"}],["path",{d:"m15 18 6-6-6-6"}]],yt=[["path",{d:"m16 3 4 4-4 4"}],["path",{d:"M20 7H4"}],["path",{d:"m8 21-4-4 4-4"}],["path",{d:"M4 17h16"}]],st=[["path",{d:"M17 12H3"}],["path",{d:"m11 18 6-6-6-6"}],["path",{d:"M21 5v14"}]],Ct=[["path",{d:"M5 12h14"}],["path",{d:"m12 5 7 7-7 7"}]],ut=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["path",{d:"M17 10V4h-2"}],["path",{d:"M15 10h4"}],["rect",{x:"15",y:"14",width:"4",height:"6",ry:"2"}]],gt=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["rect",{x:"15",y:"4",width:"4",height:"6",ry:"2"}],["path",{d:"M17 20v-6h-2"}],["path",{d:"M15 20h4"}]],S=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["path",{d:"M20 8h-5"}],["path",{d:"M15 10V6.5a2.5 2.5 0 0 1 5 0V10"}],["path",{d:"M15 14h5l-5 6h5"}]],At=[["path",{d:"m21 16-4 4-4-4"}],["path",{d:"M17 20V4"}],["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}]],Ht=[["path",{d:"m5 9 7-7 7 7"}],["path",{d:"M12 16V2"}],["circle",{cx:"12",cy:"21",r:"1"}]],Vt=[["path",{d:"m18 9-6-6-6 6"}],["path",{d:"M12 3v14"}],["path",{d:"M5 21h14"}]],St=[["path",{d:"M7 17V7h10"}],["path",{d:"M17 17 7 7"}]],w=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["path",{d:"M11 12h4"}],["path",{d:"M11 16h7"}],["path",{d:"M11 20h10"}]],wt=[["path",{d:"M7 7h10v10"}],["path",{d:"M7 17 17 7"}]],Lt=[["path",{d:"M5 3h14"}],["path",{d:"m18 13-6-6-6 6"}],["path",{d:"M12 7v14"}]],ft=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["path",{d:"M11 12h10"}],["path",{d:"M11 16h7"}],["path",{d:"M11 20h4"}]],L=[["path",{d:"m3 8 4-4 4 4"}],["path",{d:"M7 4v16"}],["path",{d:"M15 4h5l-5 6h5"}],["path",{d:"M15 20v-3.5a2.5 2.5 0 0 1 5 0V20"}],["path",{d:"M20 18h-5"}]],kt=[["path",{d:"m5 12 7-7 7 7"}],["path",{d:"M12 19V5"}]],Pt=[["path",{d:"m4 6 3-3 3 3"}],["path",{d:"M7 17V3"}],["path",{d:"m14 6 3-3 3 3"}],["path",{d:"M17 17V3"}],["path",{d:"M4 21h16"}]],Bt=[["path",{d:"M12.983 21.186a1 1 0 0 1-1.966 0 10 10 0 0 0-8.203-8.203 1 1 0 0 1 0-1.966 10 10 0 0 0 8.203-8.203 1 1 0 0 1 1.966 0 10 10 0 0 0 8.203 8.203 1 1 0 0 1 0 1.966 10 10 0 0 0-8.203 8.203"}]],zt=[["path",{d:"M12 5v14"}],["path",{d:"m18.065 8.496-12.125 7"}],["path",{d:"m5.94 8.504 12.125 7"}]],Dt=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"}]],Ft=[["circle",{cx:"12",cy:"12",r:"1"}],["path",{d:"M20.2 20.2c2.04-2.03.02-7.36-4.5-11.9-4.54-4.52-9.87-6.54-11.9-4.5-2.04 2.03-.02 7.36 4.5 11.9 4.54 4.52 9.87 6.54 11.9 4.5Z"}],["path",{d:"M15.7 15.7c4.52-4.54 6.54-9.87 4.5-11.9-2.03-2.04-7.36-.02-11.9 4.5-4.52 4.54-6.54 9.87-4.5 11.9 2.03 2.04 7.36.02 11.9-4.5Z"}]],Rt=[["path",{d:"M10 10v11"}],["path",{d:"M10 3v1.35"}],["path",{d:"M14 14v1"}],["path",{d:"M14 8v.35"}],["path",{d:"M18 5v7.35"}],["path",{d:"M2 10v3"}],["path",{d:"m2 2 20 20"}],["path",{d:"M22 10v3"}],["path",{d:"M6 6v11"}]],bt=[["path",{d:"M10 3v18"}],["path",{d:"M14 8v6.35"}],["path",{d:"m17 17 5 5"}],["path",{d:"M18 5v8.1"}],["path",{d:"M2 10v3"}],["path",{d:"M22 10v3"}],["path",{d:"m22 17-5 5"}],["path",{d:"M6 6v11"}]],Tt=[["path",{d:"M2 10v3"}],["path",{d:"M6 6v11"}],["path",{d:"M10 3v18"}],["path",{d:"M14 8v7"}],["path",{d:"M18 5v13"}],["path",{d:"M22 10v3"}]],qt=[["path",{d:"M2 13a2 2 0 0 0 2-2V7a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0V4a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0v-4a2 2 0 0 1 2-2"}]],Ut=[["path",{d:"M13.465 3.464a5 5 0 017.07 0 5 5 0 010 7.072c-1.204 1.203-2.38 2.89-2.614 4.577A8 8 0 118.886 6.08c1.687-.235 3.375-1.41 4.579-2.615"}],["circle",{cx:"10",cy:"14",r:"3"}]],Ot=[["path",{d:"m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526"}],["circle",{cx:"12",cy:"8",r:"6"}]],Gt=[["path",{d:"m14 12-8.381 8.38a1 1 0 0 1-3.001-3L11 9"}],["path",{d:"M15 15.5a.5.5 0 0 0 .5.5A6.5 6.5 0 0 0 22 9.5a.5.5 0 0 0-.5-.5h-1.672a2 2 0 0 1-1.414-.586l-5.062-5.062a1.205 1.205 0 0 0-1.704 0L9.352 5.648a1.205 1.205 0 0 0 0 1.704l5.062 5.062A2 2 0 0 1 15 13.828z"}]],f=[["path",{d:"M13.5 10.5 15 9"}],["path",{d:"M4 4v15a1 1 0 0 0 1 1h15"}],["path",{d:"M4.293 19.707 6 18"}],["path",{d:"m9 15 1.5-1.5"}]],Zt=[["path",{d:"M10 16c.5.3 1.2.5 2 .5s1.5-.2 2-.5"}],["path",{d:"M15 12h.01"}],["path",{d:"M19.38 6.813A9 9 0 0 1 20.8 10.2a2 2 0 0 1 0 3.6 9 9 0 0 1-17.6 0 2 2 0 0 1 0-3.6A9 9 0 0 1 12 3c2 0 3.5 1.1 3.5 2.5s-.9 2.5-2 2.5c-.8 0-1.5-.4-1.5-1"}],["path",{d:"M9 12h.01"}]],Et=[["path",{d:"M4 10a4 4 0 0 1 4-4h8a4 4 0 0 1 4 4v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"}],["path",{d:"M8 10h8"}],["path",{d:"M8 18h8"}],["path",{d:"M8 22v-6a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v6"}],["path",{d:"M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"}]],Wt=[["path",{d:"M2.795 16.804c-.789-.79-1.09-2.018-.442-2.928 1.933-2.713 4.414-1.05 6.145-5.378 1.868-4.67 4.608-2.366 6.592-6.107.218-.412.78-.53 1.11-.2l5.005 5.005c.789.79 1.09 2.018.442 2.928-1.933 2.713-4.414 1.05-6.145 5.378-1.868 4.67-4.608 2.366-6.591 6.107-.22.412-.78.53-1.11.2z"}],["path",{d:"M4.5 18.5c2-5 5-2 7-7s5-2 7-7"}]],It=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]],Xt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M12 7v10"}],["path",{d:"M15.4 10a4 4 0 1 0 0 4"}]],k=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"m16 9-5.5 5.5L8 12"}]],Nt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"}],["path",{d:"M12 18V6"}]],Kt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M7 12h5"}],["path",{d:"M15 9.4a4 4 0 1 0 0 5.2"}]],Jt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M8 8h8"}],["path",{d:"M8 12h8"}],["path",{d:"m13 17-5-1h1a4 4 0 0 0 0-8"}]],Qt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["line",{x1:"12",x2:"12",y1:"16",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"8",y2:"8"}]],jt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"m9 8 3 3v7"}],["path",{d:"m12 11 3-3"}],["path",{d:"M9 12h6"}],["path",{d:"M9 16h6"}]],Yt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12"}]],_t=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"m15 9-6 6"}],["path",{d:"M9 9h.01"}],["path",{d:"M15 15h.01"}]],xt=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["line",{x1:"12",x2:"12",y1:"8",y2:"16"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12"}]],a5=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M8 12h4"}],["path",{d:"M10 16V9.5a2.5 2.5 0 0 1 5 0"}],["path",{d:"M8 16h7"}]],P=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"}],["line",{x1:"12",x2:"12.01",y1:"17",y2:"17"}]],h5=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M9 16h5"}],["path",{d:"M9 12h5a2 2 0 1 0 0-4h-3v9"}]],t5=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["path",{d:"M11 17V8h4"}],["path",{d:"M11 12h3"}],["path",{d:"M9 16h4"}]],d5=[["path",{d:"M11 7v10a5 5 0 0 0 5-5"}],["path",{d:"m15 8-6 3"}],["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76"}]],c5=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}],["line",{x1:"15",x2:"9",y1:"9",y2:"15"}],["line",{x1:"9",x2:"15",y1:"9",y2:"15"}]],M5=[["path",{d:"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"}]],p5=[["path",{d:"M22 18H6a2 2 0 0 1-2-2V7a2 2 0 0 0-2-2"}],["path",{d:"M17 14V4a2 2 0 0 0-2-2h-1a2 2 0 0 0-2 2v10"}],["rect",{width:"13",height:"8",x:"8",y:"6",rx:"1"}],["circle",{cx:"18",cy:"20",r:"2"}],["circle",{cx:"9",cy:"20",r:"2"}]],l5=[["path",{d:"M12 16v1a2 2 0 0 0 2 2h1a2 2 0 0 1 2 2v1"}],["path",{d:"M12 6a2 2 0 0 1 2 2"}],["path",{d:"M18 8c0 4-3.5 8-6 8s-6-4-6-8a6 6 0 0 1 12 0"}]],i5=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M4.929 4.929 19.07 19.071"}]],n5=[["path",{d:"M4 13c3.5-2 8-2 10 2a5.5 5.5 0 0 1 8 5"}],["path",{d:"M5.15 17.89c5.52-1.52 8.65-6.89 7-12C11.55 4 11.5 2 13 2c3.22 0 5 5.5 5 8 0 6.5-4.2 12-10.49 12C5.11 22 2 22 2 20c0-1.5 1.14-1.55 3.15-2.11Z"}]],e5=[["path",{d:"M10 10.01h.01"}],["path",{d:"M10 14.01h.01"}],["path",{d:"M14 10.01h.01"}],["path",{d:"M14 14.01h.01"}],["path",{d:"M18 6v12"}],["path",{d:"M6 6v12"}],["rect",{x:"2",y:"6",width:"20",height:"12",rx:"2"}]],r5=[["path",{d:"M6 5a2 2 0 0 1 4 0v12a4 4 0 0 0 8 0 2 2 0 0 0-4 0"}],["path",{d:"M6 9h12"}]],o5=[["path",{d:"M12 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5"}],["path",{d:"m16 19 3 3 3-3"}],["path",{d:"M18 12h.01"}],["path",{d:"M19 16v6"}],["path",{d:"M6 12h.01"}],["circle",{cx:"12",cy:"12",r:"2"}]],v5=[["path",{d:"M12 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5"}],["path",{d:"M18 12h.01"}],["path",{d:"M19 22v-6"}],["path",{d:"m22 19-3-3-3 3"}],["path",{d:"M6 12h.01"}],["circle",{cx:"12",cy:"12",r:"2"}]],$5=[["path",{d:"M11.748 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4.875"}],["path",{d:"m16 19 2 2 4-4"}],["path",{d:"M18 12h.01"}],["path",{d:"M6 12h.01"}],["circle",{cx:"12",cy:"12",r:"2"}]],m5=[["path",{d:"M13 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5"}],["path",{d:"m17 17 5 5"}],["path",{d:"M18 12h.01"}],["path",{d:"m22 17-5 5"}],["path",{d:"M6 12h.01"}],["circle",{cx:"12",cy:"12",r:"2"}]],y5=[["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M6 12h.01M18 12h.01"}]],s5=[["path",{d:"M3 5v14"}],["path",{d:"M8 5v14"}],["path",{d:"M12 5v14"}],["path",{d:"M17 5v14"}],["path",{d:"M21 5v14"}]],C5=[["path",{d:"M10.5 2a34.5 34.5 0 000 20"}],["path",{d:"M13.5 2a34.5 34.5 0 010 20"}],["path",{d:"M4.67 17h14.67"}],["path",{d:"M4.67 7h14.67"}],["path",{d:"M6.23 3.06a19 19 0 000 17.88A2 2 0 008 22h8a2 2 0 001.77-1.06 19 19 0 000-17.88A2 2 0 0016 2H8a2 2 0 00-1.77 1.06"}]],u5=[["path",{d:"M4 20h16"}],["path",{d:"m6 16 6-12 6 12"}],["path",{d:"M8 12h8"}]],g5=[["path",{d:"M10 4 8 6"}],["path",{d:"M17 19v2"}],["path",{d:"M2 12h20"}],["path",{d:"M7 19v2"}],["path",{d:"M9 5 7.621 3.621A2.121 2.121 0 0 0 4 5v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5"}]],A5=[["path",{d:"m11 7-3 5h4l-3 5"}],["path",{d:"M14.856 6H16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.935"}],["path",{d:"M22 14v-4"}],["path",{d:"M5.14 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.936"}]],H5=[["path",{d:"M10 10v4"}],["path",{d:"M14 10v4"}],["path",{d:"M22 14v-4"}],["path",{d:"M6 10v4"}],["rect",{x:"2",y:"6",width:"16",height:"12",rx:"2"}]],V5=[["path",{d:"M22 14v-4"}],["path",{d:"M6 14v-4"}],["rect",{x:"2",y:"6",width:"16",height:"12",rx:"2"}]],S5=[["path",{d:"M10 14v-4"}],["path",{d:"M22 14v-4"}],["path",{d:"M6 14v-4"}],["rect",{x:"2",y:"6",width:"16",height:"12",rx:"2"}]],w5=[["path",{d:"M10 9v6"}],["path",{d:"M12.543 6H16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-3.605"}],["path",{d:"M22 14v-4"}],["path",{d:"M7 12h6"}],["path",{d:"M7.606 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3.606"}]],L5=[["path",{d:"M10 17h.01"}],["path",{d:"M10 7v6"}],["path",{d:"M14 6h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2"}],["path",{d:"M22 14v-4"}],["path",{d:"M6 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2"}]],f5=[["path",{d:"M 22 14 L 22 10"}],["rect",{x:"2",y:"6",width:"16",height:"12",rx:"2"}]],k5=[["path",{d:"M4.5 3h15"}],["path",{d:"M6 3v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V3"}],["path",{d:"M6 14h12"}]],P5=[["path",{d:"M9 9c-.64.64-1.521.954-2.402 1.165A6 6 0 0 0 8 22a13.96 13.96 0 0 0 9.9-4.1"}],["path",{d:"M10.75 5.093A6 6 0 0 1 22 8c0 2.411-.61 4.68-1.683 6.66"}],["path",{d:"M5.341 10.62a4 4 0 0 0 6.487 1.208M10.62 5.341a4.015 4.015 0 0 1 2.039 2.04"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],B5=[["path",{d:"M10.165 6.598C9.954 7.478 9.64 8.36 9 9c-.64.64-1.521.954-2.402 1.165A6 6 0 0 0 8 22c7.732 0 14-6.268 14-14a6 6 0 0 0-11.835-1.402Z"}],["path",{d:"M5.341 10.62a4 4 0 1 0 5.279-5.28"}]],z5=[["path",{d:"M2 20v-8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v8"}],["path",{d:"M4 10V6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4"}],["path",{d:"M12 4v6"}],["path",{d:"M2 18h20"}]],D5=[["path",{d:"M3 20v-8a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v8"}],["path",{d:"M5 10V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v4"}],["path",{d:"M3 18h18"}]],F5=[["path",{d:"M2 4v16"}],["path",{d:"M2 8h18a2 2 0 0 1 2 2v10"}],["path",{d:"M2 17h20"}],["path",{d:"M6 8v9"}]],R5=[["path",{d:"M11.771 6.109a2.5 2.5 0 0 1 3.12 3.12"}],["path",{d:"M17.852 12.185a6.5 6.5 0 0 0-9.035-9.04"}],["path",{d:"M18.013 18.013C15.029 20.349 10.831 22 7 22a3 3 0 0 1-2.68-1.66L2.4 16.5"}],["path",{d:"m18.5 6 2.19 4.5a6.48 6.48 0 0 1-.139 4.393"}],["path",{d:"m2 2 20 20"}],["path",{d:"M6.355 6.37a7 7 0 0 0-.075.23c-1.1 3.13-.78 3.9-3.18 6.08A3 3 0 0 0 5 18c3.356 0 6.993-1.267 9.85-3.151"}]],b5=[["path",{d:"M16.4 13.7A6.5 6.5 0 1 0 6.28 6.6c-1.1 3.13-.78 3.9-3.18 6.08A3 3 0 0 0 5 18c4 0 8.4-1.8 11.4-4.3"}],["path",{d:"m18.5 6 1.754 3.5a6.48 6.48 0 0 1-1.854 8.2C15.4 20.2 11 22 7 22a3 3 0 0 1-2.68-1.66L2.4 16.5"}],["circle",{cx:"12.5",cy:"8.5",r:"2.5"}]],T5=[["path",{d:"M13 13v5"}],["path",{d:"M17 11.47V8"}],["path",{d:"M17 11h1a3 3 0 0 1 2.745 4.211"}],["path",{d:"m2 2 20 20"}],["path",{d:"M5 8v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-3"}],["path",{d:"M7.536 7.535C6.766 7.649 6.154 8 5.5 8a2.5 2.5 0 0 1-1.768-4.268"}],["path",{d:"M8.727 3.204C9.306 2.767 9.885 2 11 2c1.56 0 2 1.5 3 1.5s1.72-.5 2.5-.5a1 1 0 1 1 0 5c-.78 0-1.5-.5-2.5-.5a3.149 3.149 0 0 0-.842.12"}],["path",{d:"M9 14.6V18"}]],q5=[["path",{d:"M17 11h1a3 3 0 0 1 0 6h-1"}],["path",{d:"M9 12v6"}],["path",{d:"M13 12v6"}],["path",{d:"M14 7.5c-1 0-1.44.5-3 .5s-2-.5-3-.5-1.72.5-2.5.5a2.5 2.5 0 0 1 0-5c.78 0 1.57.5 2.5.5S9.44 2 11 2s2 1.5 3 1.5 1.72-.5 2.5-.5a2.5 2.5 0 0 1 0 5c-.78 0-1.5-.5-2.5-.5Z"}],["path",{d:"M5 8v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8"}]],U5=[["path",{d:"M3 16 L13 16"}],["path",{d:"M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7"}]],O5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"m15 8 2 2 4-4"}],["path",{d:"M16.8607 4.4824A6 6 0 0 0 6 8C6 12.499 4.589 13.956 3.262 15.326"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17H20A1 1 0 0 0 20.74 15.327C20.209 14.779 19.665 14.218 19.203 13.454"}]],G5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M11.68 2.009A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673c-.824-.85-1.678-1.731-2.21-3.348"}],["circle",{cx:"18",cy:"5",r:"3"}]],Z5=[["path",{d:"M18.518 17.347A7 7 0 0 1 14 19"}],["path",{d:"M18.8 4A11 11 0 0 1 20 9"}],["path",{d:"M9 9h.01"}],["circle",{cx:"20",cy:"16",r:"2"}],["circle",{cx:"9",cy:"9",r:"7"}],["rect",{x:"4",y:"16",width:"10",height:"6",rx:"2"}]],E5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M15 8h6"}],["path",{d:"M16.243 3.757A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673A9.4 9.4 0 0 1 18.667 12"}]],W5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05"}]],I5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M15 8h6"}],["path",{d:"M18 5v6"}],["path",{d:"M20.002 14.464a9 9 0 0 0 .738.863A1 1 0 0 1 20 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 8.75-5.332"}]],X5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M22 8c0-2.3-.8-4.3-2-6"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"}],["path",{d:"M4 2C2.8 3.7 2 5.7 2 8"}]],N5=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"}]],B=[["rect",{width:"13",height:"7",x:"3",y:"3",rx:"1"}],["path",{d:"m22 15-3-3 3-3"}],["rect",{width:"13",height:"7",x:"3",y:"14",rx:"1"}]],z=[["rect",{width:"13",height:"7",x:"8",y:"3",rx:"1"}],["path",{d:"m2 9 3 3-3 3"}],["rect",{width:"13",height:"7",x:"8",y:"14",rx:"1"}]],K5=[["rect",{width:"7",height:"13",x:"3",y:"3",rx:"1"}],["path",{d:"m9 22 3-3 3 3"}],["rect",{width:"7",height:"13",x:"14",y:"3",rx:"1"}]],J5=[["rect",{width:"7",height:"13",x:"3",y:"8",rx:"1"}],["path",{d:"m15 2-3 3-3-3"}],["rect",{width:"7",height:"13",x:"14",y:"8",rx:"1"}]],Q5=[["path",{d:"M12.409 13.017A5 5 0 0 1 22 15c0 3.866-4 7-9 7-4.077 0-8.153-.82-10.371-2.462-.426-.316-.631-.832-.62-1.362C2.118 12.723 2.627 2 10 2a3 3 0 0 1 3 3 2 2 0 0 1-2 2c-1.105 0-1.64-.444-2-1"}],["path",{d:"M15 14a5 5 0 0 0-7.584 2"}],["path",{d:"M9.964 6.825C8.019 7.977 9.5 13 8 15"}]],j5=[["circle",{cx:"18.5",cy:"17.5",r:"3.5"}],["circle",{cx:"5.5",cy:"17.5",r:"3.5"}],["circle",{cx:"15",cy:"5",r:"1"}],["path",{d:"M12 17.5V14l-3-3 4-3 2 3h2"}]],Y5=[["rect",{x:"14",y:"14",width:"4",height:"6",rx:"2"}],["rect",{x:"6",y:"4",width:"4",height:"6",rx:"2"}],["path",{d:"M6 20h4"}],["path",{d:"M14 10h4"}],["path",{d:"M6 14h2v6"}],["path",{d:"M14 4h2v6"}]],_5=[["circle",{cx:"12",cy:"11.9",r:"2"}],["path",{d:"M6.7 3.4c-.9 2.5 0 5.2 2.2 6.7C6.5 9 3.7 9.6 2 11.6"}],["path",{d:"m8.9 10.1 1.4.8"}],["path",{d:"M17.3 3.4c.9 2.5 0 5.2-2.2 6.7 2.4-1.2 5.2-.6 6.9 1.5"}],["path",{d:"m15.1 10.1-1.4.8"}],["path",{d:"M16.7 20.8c-2.6-.4-4.6-2.6-4.7-5.3-.2 2.6-2.1 4.8-4.7 5.2"}],["path",{d:"M12 13.9v1.6"}],["path",{d:"M13.5 5.4c-1-.2-2-.2-3 0"}],["path",{d:"M17 16.4c.7-.7 1.2-1.6 1.5-2.5"}],["path",{d:"M5.5 13.9c.3.9.8 1.8 1.5 2.5"}]],x5=[["path",{d:"M10 10h4"}],["path",{d:"M19 7V4a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3"}],["path",{d:"M20 21a2 2 0 0 0 2-2v-3.851c0-1.39-2-2.962-2-4.829V8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v11a2 2 0 0 0 2 2z"}],["path",{d:"M 22 16 L 2 16"}],["path",{d:"M4 21a2 2 0 0 1-2-2v-3.851c0-1.39 2-2.962 2-4.829V8a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v11a2 2 0 0 1-2 2z"}],["path",{d:"M9 7V4a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v3"}]],a4=[["path",{d:"M16 7h.01"}],["path",{d:"M3.4 18H12a8 8 0 0 0 8-8V7a4 4 0 0 0-7.28-2.3L2 20"}],["path",{d:"m20 7 2 .5-2 .5"}],["path",{d:"M10 18v3"}],["path",{d:"M14 17.75V21"}],["path",{d:"M7 18a6 6 0 0 0 3.84-10.61"}]],h4=[["path",{d:"M12 18v4"}],["path",{d:"m17 18 1.956-11.468"}],["path",{d:"m3 8 7.82-5.615a2 2 0 0 1 2.36 0L21 8"}],["path",{d:"M4 18h16"}],["path",{d:"M7 18 5.044 6.532"}],["circle",{cx:"12",cy:"10",r:"2"}]],t4=[["path",{d:"M11.767 19.089c4.924.868 6.14-6.025 1.216-6.894m-1.216 6.894L5.86 18.047m5.908 1.042-.347 1.97m1.563-8.864c4.924.869 6.14-6.025 1.215-6.893m-1.215 6.893-3.94-.694m5.155-6.2L8.29 4.26m5.908 1.042.348-1.97M7.48 20.364l3.126-17.727"}]],d4=[["circle",{cx:"15",cy:"9",r:"7"}],["circle",{cx:"9",cy:"15",r:"7"}]],c4=[["path",{d:"M8 14a2 2 0 0 0-1.963 1.615l-1.018 5.193A1 1 0 0 0 6 22h12a1 1 0 0 0 .981-1.192l-1.018-5.193A2 2 0 0 0 16 14z"}],["path",{d:"m17 2-1 12"}],["path",{d:"M8.006 14 7 2"}],["path",{d:"M7.565 8.787A5 5 0 0 0 12 8a5 5 0 0 1 4.56-.75"}],["path",{d:"M19 2H5a2 2 0 0 0-2 2v5a2 2 0 0 0 .688 1.5"}],["path",{d:"M12 18h.01"}]],M4=[["path",{d:"M3 3h18"}],["path",{d:"M20 7H8"}],["path",{d:"M20 11H8"}],["path",{d:"M10 19h10"}],["path",{d:"M8 15h12"}],["path",{d:"M4 3v14"}],["circle",{cx:"4",cy:"19",r:"2"}]],p4=[["path",{d:"M10 22V7a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5a1 1 0 0 0-1-1H2"}],["rect",{x:"14",y:"2",width:"8",height:"8",rx:"1"}]],l4=[["path",{d:"m7 7 10 10-5 5V2l5 5L7 17"}],["line",{x1:"18",x2:"21",y1:"12",y2:"12"}],["line",{x1:"3",x2:"6",y1:"12",y2:"12"}]],i4=[["path",{d:"m17 17-5 5V12l-5 5"}],["path",{d:"m2 2 20 20"}],["path",{d:"M14.5 9.5 17 7l-5-5v4.5"}]],n4=[["path",{d:"m7 7 10 10-5 5V2l5 5L7 17"}],["path",{d:"M20.83 14.83a4 4 0 0 0 0-5.66"}],["path",{d:"M18 12h.01"}]],e4=[["path",{d:"m7 7 10 10-5 5V2l5 5L7 17"}]],r4=[["path",{d:"M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8"}]],o4=[["path",{d:"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"}],["circle",{cx:"12",cy:"12",r:"4"}]],v4=[["circle",{cx:"11",cy:"13",r:"9"}],["path",{d:"M14.35 4.65 16.3 2.7a2.41 2.41 0 0 1 3.4 0l1.6 1.6a2.4 2.4 0 0 1 0 3.4l-1.95 1.95"}],["path",{d:"m22 2-1.5 1.5"}]],$4=[["path",{d:"M14 4.5a1 1 0 0 1 5 0 .5.5 0 0 0 .5.5 1 1 0 0 1 0 5c-.81 0-1.8-.7-2.5 0l-1.958 1.957a.15.15 0 0 1-.252-.072l-.493-2.07a.15.15 0 0 0-.111-.112l-2.072-.494a.15.15 0 0 1-.072-.252L14 7c.7-.7 0-1.69 0-2.5"}],["path",{d:"m16 20-1-2"}],["path",{d:"m20 16-2-1"}],["path",{d:"m4 8 2 1"}],["path",{d:"m8 4 1 2"}],["path",{d:"M9.698 14.19a.15.15 0 0 0 .112.112l2.074.489a.15.15 0 0 1 .072.252L10 17c-.7.7 0 1.69 0 2.5a1 1 0 0 1-5 0 .495.495 0 0 0-.5-.5 1 1 0 0 1 0-5c.81 0 1.8.7 2.5 0l1.956-1.957a.15.15 0 0 1 .252.072z"}]],m4=[["path",{d:"M17 10c.7-.7 1.69 0 2.5 0a2.5 2.5 0 1 0 0-5 .5.5 0 0 1-.5-.5 2.5 2.5 0 1 0-5 0c0 .81.7 1.8 0 2.5l-7 7c-.7.7-1.69 0-2.5 0a2.5 2.5 0 0 0 0 5c.28 0 .5.22.5.5a2.5 2.5 0 1 0 5 0c0-.81-.7-1.8 0-2.5Z"}]],y4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"m8 13 4-7 4 7"}],["path",{d:"M9.1 11h5.7"}]],s4=[["path",{d:"M12 13h.01"}],["path",{d:"M12 6v3"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}]],C4=[["path",{d:"M12 6v7"}],["path",{d:"M16 8v3"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M8 8v3"}]],D=[["path",{d:"M10 2v7.751a.25.25 0 00.407.195l2.28-1.834a.5.5 0 01.627 0l2.28 1.834A.25.25 0 0016 9.751V2"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 016.5 2H19a1 1 0 011 1v18a1 1 0 01-1 1H6.5a1 1 0 010-5H20"}]],u4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"m9 9.5 2 2 4-4"}]],g4=[["path",{d:"M5 7a2 2 0 0 0-2 2v11"}],["path",{d:"M5.803 18H5a2 2 0 0 0 0 4h9.5a.5.5 0 0 0 .5-.5V21"}],["path",{d:"M9 15V4a2 2 0 0 1 2-2h9.5a.5.5 0 0 1 .5.5v14a.5.5 0 0 1-.5.5H11a2 2 0 0 1 0-4h10"}]],F=[["path",{d:"M12 17h1.5"}],["path",{d:"M12 22h1.5"}],["path",{d:"M12 2h1.5"}],["path",{d:"M17.5 22H19a1 1 0 0 0 1-1"}],["path",{d:"M17.5 2H19a1 1 0 0 1 1 1v1.5"}],["path",{d:"M20 14v3h-2.5"}],["path",{d:"M20 8.5V10"}],["path",{d:"M4 10V8.5"}],["path",{d:"M4 19.5V14"}],["path",{d:"M4 4.5A2.5 2.5 0 0 1 6.5 2H8"}],["path",{d:"M8 22H6.5a1 1 0 0 1 0-5H8"}]],A4=[["path",{d:"M12 13V7"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"m9 10 3 3 3-3"}]],H4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M8 12v-2a4 4 0 0 1 8 0v2"}],["circle",{cx:"15",cy:"12",r:"1"}],["circle",{cx:"9",cy:"12",r:"1"}]],V4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M8.62 9.8A2.25 2.25 0 1 1 12 6.836a2.25 2.25 0 1 1 3.38 2.966l-2.626 2.856a.998.998 0 0 1-1.507 0z"}]],S4=[["path",{d:"m20 13.7-2.1-2.1a2 2 0 0 0-2.8 0L9.7 17"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["circle",{cx:"10",cy:"8",r:"2"}]],w4=[["path",{d:"M13 2H6.5A2.5 2.5 0 0 0 4 4.5v15"}],["path",{d:"M17 2v6"}],["path",{d:"M17 4h2"}],["path",{d:"M20 15.2V21a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["circle",{cx:"17",cy:"10",r:"2"}]],L4=[["path",{d:"M18 6V4a2 2 0 1 0-4 0v2"}],["path",{d:"M20 15v6a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H10"}],["rect",{x:"12",y:"6",width:"8",height:"5",rx:"1"}]],f4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M9 10h6"}]],k4=[["path",{d:"M12 5v16"}],["path",{d:"m16 12 2 2 4-4"}],["path",{d:"M22 6V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2h4.001A2 2 0 0022 17v-1.344"}]],P4=[["path",{d:"M12 5v16"}],["path",{d:"M16 13h2"}],["path",{d:"M16 9h2"}],["path",{d:"M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z"}],["path",{d:"M6 13h2"}],["path",{d:"M6 9h2"}]],B4=[["path",{d:"M12 5v16"}],["path",{d:"M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z"}]],z4=[["path",{d:"M11 22H5.5a1 1 0 0 1 0-5h4.501"}],["path",{d:"m21 22-1.879-1.878"}],["path",{d:"M3 19.5v-15A2.5 2.5 0 0 1 5.5 2H18a1 1 0 0 1 1 1v8"}],["circle",{cx:"17",cy:"18",r:"3"}]],D4=[["path",{d:"M12 7v6"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M9 10h6"}]],F4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M8 11h8"}],["path",{d:"M8 7h6"}]],R4=[["path",{d:"M10 13h4"}],["path",{d:"M12 6v7"}],["path",{d:"M16 8V6H8v2"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}]],b4=[["path",{d:"M12 13V7"}],["path",{d:"M18 2h1a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2"}],["path",{d:"m9 10 3-3 3 3"}],["path",{d:"m9 5 3-3 3 3"}]],T4=[["path",{d:"M12 13V7"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"m9 10 3-3 3 3"}]],q4=[["path",{d:"M15 13a3 3 0 1 0-6 0"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["circle",{cx:"12",cy:"8",r:"2"}]],U4=[["path",{d:"m14.5 7.5-5 5"}],["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}],["path",{d:"m9.5 7.5 5 5"}]],O4=[["path",{d:"M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"}]],G4=[["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"}],["path",{d:"m9 10 2 2 4-4"}]],Z4=[["path",{d:"M15 10H9"}],["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"}]],E4=[["path",{d:"M19 19v1a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.656 3H17a2 2 0 0 1 2 2v8.344"}]],W4=[["path",{d:"M12 7v6"}],["path",{d:"M15 10H9"}],["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"}]],I4=[["path",{d:"m14.5 7.5-5 5"}],["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"}],["path",{d:"m9.5 7.5 5 5"}]],X4=[["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"}]],N4=[["path",{d:"M4 9V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4"}],["path",{d:"M8 8v1"}],["path",{d:"M12 8v1"}],["path",{d:"M16 8v1"}],["rect",{width:"20",height:"12",x:"2",y:"9",rx:"2"}],["circle",{cx:"8",cy:"15",r:"2"}],["circle",{cx:"16",cy:"15",r:"2"}]],K4=[["path",{d:"M12 6V2H8"}],["path",{d:"M15 11v2"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"M20 16a2 2 0 0 1-2 2H8.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 4 20.286V8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2z"}],["path",{d:"M9 11v2"}]],J4=[["path",{d:"M13.67 8H18a2 2 0 0 1 2 2v4.33"}],["path",{d:"M2 14h2"}],["path",{d:"M20 14h2"}],["path",{d:"M22 22 2 2"}],["path",{d:"M8 8H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 1.414-.586"}],["path",{d:"M9 13v2"}],["path",{d:"M9.67 4H12v2.33"}]],Q4=[["path",{d:"M12 8V4H8"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2"}],["path",{d:"M2 14h2"}],["path",{d:"M20 14h2"}],["path",{d:"M15 13v2"}],["path",{d:"M9 13v2"}]],j4=[["path",{d:"M10 3a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a6 6 0 0 0 1.2 3.6l.6.8A6 6 0 0 1 17 13v8a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1v-8a6 6 0 0 1 1.2-3.6l.6-.8A6 6 0 0 0 10 5z"}],["path",{d:"M17 13h-4a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h4"}]],Y4=[["path",{d:"M17 3h4v4"}],["path",{d:"M18.575 11.082a13 13 0 0 1 1.048 9.027 1.17 1.17 0 0 1-1.914.597L14 17"}],["path",{d:"M7 10 3.29 6.29a1.17 1.17 0 0 1 .6-1.91 13 13 0 0 1 9.03 1.05"}],["path",{d:"M7 14a1.7 1.7 0 0 0-1.207.5l-2.646 2.646A.5.5 0 0 0 3.5 18H5a1 1 0 0 1 1 1v1.5a.5.5 0 0 0 .854.354L9.5 18.207A1.7 1.7 0 0 0 10 17v-2a1 1 0 0 0-1-1z"}],["path",{d:"M9.707 14.293 21 3"}]],_4=[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"}],["path",{d:"m3.3 7 8.7 5 8.7-5"}],["path",{d:"M12 22V12"}]],x4=[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"}],["path",{d:"m7 16.5-4.74-2.85"}],["path",{d:"m7 16.5 5-3"}],["path",{d:"M7 16.5v5.17"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"}],["path",{d:"m17 16.5-5-3"}],["path",{d:"m17 16.5 4.74-2.85"}],["path",{d:"M17 16.5v5.17"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"}],["path",{d:"M12 8 7.26 5.15"}],["path",{d:"m12 8 4.74-2.85"}],["path",{d:"M12 13.5V8"}]],R=[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"}],["path",{d:"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"}]],a3=[["path",{d:"M16 3h3a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-3"}],["path",{d:"M8 21H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h3"}]],h3=[["path",{d:"M12 13h4"}],["path",{d:"M12 18h6a2 2 0 012 2v1"}],["path",{d:"M12 8h8"}],["path",{d:"M12 9a4.17 4.17 0 01-3 4"}],["path",{d:"M16 8V5a2 2 0 012-2"}],["path",{d:"M6 18a4 4 0 01-2-7.465"}],["path",{d:"M6.004 5.125a4 4 0 00-2.526 5.77"}],["path",{d:"M6.294 6.294A3 3 0 1112 5v13a4 4 0 11-7.967-.518"}],["circle",{cx:"16",cy:"13",r:"0.5",fill:"currentColor"}],["circle",{cx:"18",cy:"3",r:"0.5",fill:"currentColor"}],["circle",{cx:"20",cy:"21",r:"0.5",fill:"currentColor"}],["circle",{cx:"20",cy:"8",r:"0.5",fill:"currentColor"}]],t3=[["path",{d:"m10.852 14.772-.383.923"}],["path",{d:"m10.852 9.228-.383-.923"}],["path",{d:"m13.148 14.772.382.924"}],["path",{d:"m13.53 8.305-.382.923"}],["path",{d:"m14.772 10.852.923-.383"}],["path",{d:"m14.772 13.148.923.383"}],["path",{d:"M17.997 5.125a4 4 0 012.526 5.77"}],["path",{d:"M18 18a4 4 0 002-7.464"}],["path",{d:"M4.033 17.483A4 4 0 0011.46 20c.184-.31.896-.31 1.076 0a4 4 0 007.43-2.517"}],["path",{d:"M6 18a4 4 0 01-2-7.464"}],["path",{d:"M6.002 5.125a4 4 0 00-2.525 5.77"}],["path",{d:"M6.293 6.294a3 3 0 01-.287-1.098l-.004-.07a3 3 0 015.968-.543c.05.02.057.01.059 0a3 3 0 015.968.542l-.004.071a3 3 0 01-.287 1.098"}],["path",{d:"m9.228 10.852-.923-.383"}],["path",{d:"m9.228 13.148-.923.383"}],["circle",{cx:"12",cy:"12",r:"3"}]],d3=[["path",{d:"M12 18v-6"}],["path",{d:"M12 5v1.344"}],["path",{d:"M17.707 6.293A3 3 0 1012 5a3 3 0 00-4.124-2.781"}],["path",{d:"M17.997 5.125a4 4 0 012.526 5.77"}],["path",{d:"M19.644 19.644A4 4 0 0112 18a4 4 0 11-7.967-.517"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20 10.536a4 4 0 011.533 5.34"}],["path",{d:"M3.477 10.895a4 4 0 011.876-5.542"}],["path",{d:"M6 18a4 4 0 01-2-7.464"}],["path",{d:"M9 13a4.17 4.17 0 002.166-1.5 4 4 0 00.138-.196"}]],c3=[["path",{d:"M12 18V5"}],["path",{d:"M15 13a4.17 4.17 0 01-3-4 4.17 4.17 0 01-3 4"}],["path",{d:"M17.707 6.294A3 3 0 1012 5a3 3 0 10-5.707 1.294"}],["path",{d:"M17.996 5.125a4 4 0 012.526 5.77"}],["path",{d:"M18 18a4 4 0 002-7.465"}],["path",{d:"M19.967 17.482a4 4 0 11-7.967.517 4 4 0 11-7.967-.517"}],["path",{d:"M6 18a4 4 0 01-2-7.465"}],["path",{d:"M6.004 5.125a4 4 0 00-2.526 5.77"}]],M3=[["path",{d:"M16 3v2.107"}],["path",{d:"M17 9c1 3 2.5 3.5 3.5 4.5A5 5 0 0 1 22 17a5 5 0 0 1-10 0c0-.3 0-.6.1-.9a2 2 0 1 0 3.3-2C13 11.5 16 9 17 9"}],["path",{d:"M21 8.274V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.938"}],["path",{d:"M3 15h5.253"}],["path",{d:"M3 9h8.228"}],["path",{d:"M8 15v6"}],["path",{d:"M8 3v6"}]],p3=[["path",{d:"M12 9v1.258"}],["path",{d:"M16 3v5.46"}],["path",{d:"M21 9.118V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h5.75"}],["path",{d:"M22 17.5c0 2.499-1.75 3.749-3.83 4.474a.5.5 0 0 1-.335-.005c-2.085-.72-3.835-1.97-3.835-4.47V14a.5.5 0 0 1 .5-.499c1 0 2.25-.6 3.12-1.36a.6.6 0 0 1 .76-.001c.875.765 2.12 1.36 3.12 1.36a.5.5 0 0 1 .5.5z"}],["path",{d:"M3 15h7"}],["path",{d:"M3 9h12.142"}],["path",{d:"M8 15v6"}],["path",{d:"M8 3v6"}]],l3=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 9v6"}],["path",{d:"M16 15v6"}],["path",{d:"M16 3v6"}],["path",{d:"M3 15h18"}],["path",{d:"M3 9h18"}],["path",{d:"M8 15v6"}],["path",{d:"M8 3v6"}]],i3=[["path",{d:"M10 9.728V16"}],["path",{d:"M14 9.728V16"}],["path",{d:"M18 20V4"}],["path",{d:"m22 11-4-4A7.5 7.5 0 0 1 6 7l-4 4"}],["path",{d:"M22 16H2"}],["path",{d:"M6 20V4"}]],n3=[["path",{d:"M12 12h.01"}],["path",{d:"M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"}],["path",{d:"M22 13a18.15 18.15 0 0 1-20 0"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2"}]],e3=[["path",{d:"M10 20v2"}],["path",{d:"M14 20v2"}],["path",{d:"M18 20v2"}],["path",{d:"M21 20H3"}],["path",{d:"M6 20v2"}],["path",{d:"M8 16V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v12"}],["rect",{x:"4",y:"6",width:"16",height:"10",rx:"2"}]],r3=[["path",{d:"M12 11v4"}],["path",{d:"M14 13h-4"}],["path",{d:"M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"}],["path",{d:"M18 6v14"}],["path",{d:"M6 6v14"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2"}]],o3=[["path",{d:"M13.354 20H4a2 2 0 01-2-2V8a2 2 0 012-2h16a2 2 0 012 2v3.354"}],["path",{d:"M16 11.354V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v16"}],["path",{d:"M16 17h6"}],["path",{d:"M19 14v6"}]],v3=[["path",{d:"M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2"}]],$3=[["rect",{x:"8",y:"8",width:"8",height:"8",rx:"2"}],["path",{d:"M4 10a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2"}],["path",{d:"M14 20a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2"}]],m3=[["path",{d:"M10 13a3 3 0 01-2.121-5.121"}],["path",{d:"M15.606 14.204c-3.5 1.5-5.899 4.503-8.899 7.503A1 1 0 016 22c-2 0-4-2-4-4a1 1 0 01.293-.707c1.911-1.911 3.823-3.578 5.347-5.441"}],["path",{d:"M16.573 14.737A4 4 0 0114 11"}],["path",{d:"M7.14 10.907a4 4 0 112.756-7.43A4 4 0 0116.7 4.48a2 2 0 012.82 2.82 4 4 0 011.002 6.805 4 4 0 11-7.51 1.59"}]],y3=[["path",{d:"M11 2v2"}],["path",{d:"M12 3h-2"}],["path",{d:"M13.5 10.5 22 2"}],["path",{d:"M14.734 13.841a2 2 0 00-.314-2.42L12.58 9.58a2 2 0 00-2.421-.314l-7.657 4.461A1 1 0 002.3 15.3l6.403 6.403a1 1 0 001.571-.204z"}],["path",{d:"M20 15v4"}],["path",{d:"M22 17h-4"}],["path",{d:"M4 4v4"}],["path",{d:"m5 18 2-2"}],["path",{d:"M6 6H2"}],["path",{d:"m7.699 10.7 5.602 5.601"}]],s3=[["path",{d:"m16 22-1-4"}],["path",{d:"M19 14a1 1 0 0 0 1-1v-1a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v1a1 1 0 0 0 1 1"}],["path",{d:"M19 14H5l-1.973 6.767A1 1 0 0 0 4 22h16a1 1 0 0 0 .973-1.233z"}],["path",{d:"m8 22 1-4"}]],C3=[["path",{d:"M13.5 10.5 22 2"}],["path",{d:"M14.734 13.841a2 2 0 00-.314-2.42L12.58 9.58a2 2 0 00-2.421-.314l-7.657 4.461A1 1 0 002.3 15.3l6.403 6.403a1 1 0 001.571-.204z"}],["path",{d:"m5 18 2-2"}],["path",{d:"m7.699 10.7 5.602 5.601"}]],u3=[["path",{d:"m11 10 3 3"}],["path",{d:"M6.5 21A3.5 3.5 0 1 0 3 17.5a2.62 2.62 0 0 1-.708 1.792A1 1 0 0 0 3 21z"}],["path",{d:"M9.969 17.031 21.378 5.624a1 1 0 0 0-3.002-3.002L6.967 14.031"}]],g3=[["path",{d:"M7.001 15.085A1.5 1.5 0 0 1 9 16.5"}],["circle",{cx:"18.5",cy:"8.5",r:"3.5"}],["circle",{cx:"7.5",cy:"16.5",r:"5.5"}],["circle",{cx:"7.5",cy:"4.5",r:"2.5"}]],A3=[["path",{d:"M12 20v-8"}],["path",{d:"M12.656 7H14a4 4 0 0 1 4 4v1.344"}],["path",{d:"M14.12 3.88 16 2"}],["path",{d:"M17.123 17.123A6 6 0 0 1 6 14v-3a4 4 0 0 1 1.72-3.287"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97"}],["path",{d:"M22 13h-3.344"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97"}],["path",{d:"M6 13H2"}],["path",{d:"m8 2 1.88 1.88"}],["path",{d:"M9.712 4.06A3 3 0 0 1 15 6v1.13"}]],H3=[["path",{d:"M12 20v-9"}],["path",{d:"M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z"}],["path",{d:"M14.12 3.88 16 2"}],["path",{d:"M21 21a4 4 0 0 0-3.81-4"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97"}],["path",{d:"M22 13h-4"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97"}],["path",{d:"M6 13H2"}],["path",{d:"m8 2 1.88 1.88"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13"}]],V3=[["path",{d:"M10 19.655A6 6 0 0 1 6 14v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 3.97"}],["path",{d:"M14 15.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997a1 1 0 0 1-1.517-.86z"}],["path",{d:"M14.12 3.88 16 2"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97"}],["path",{d:"M6 13H2"}],["path",{d:"m8 2 1.88 1.88"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13"}]],S3=[["path",{d:"M10 12h4"}],["path",{d:"M10 21v-3a2 2 0 013.05-1.702"}],["path",{d:"M10 8h4"}],["path",{d:"M16 19h6"}],["path",{d:"M18 7h2a2 2 0 012 2v4.355"}],["path",{d:"M19 16v6"}],["path",{d:"M6 10H4a2 2 0 00-2 2v7a2 2 0 002 2h8.535"}],["path",{d:"M6 21V5a2 2 0 012-2h8a2 2 0 012 2v7.126"}]],b=[["path",{d:"M10 12h4"}],["path",{d:"M10 8h4"}],["path",{d:"M14 21v-3a2 2 0 0 0-4 0v3"}],["path",{d:"M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2"}],["path",{d:"M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"}]],w3=[["path",{d:"M12 10h.01"}],["path",{d:"M12 14h.01"}],["path",{d:"M12 6h.01"}],["path",{d:"M16 10h.01"}],["path",{d:"M16 14h.01"}],["path",{d:"M16 6h.01"}],["path",{d:"M8 10h.01"}],["path",{d:"M8 14h.01"}],["path",{d:"M8 6h.01"}],["path",{d:"M9 22v-3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"}],["rect",{x:"4",y:"2",width:"16",height:"20",rx:"2"}]],L3=[["path",{d:"M4 6 2 7"}],["path",{d:"M10 6h4"}],["path",{d:"m22 7-2-1"}],["rect",{width:"16",height:"16",x:"4",y:"3",rx:"2"}],["path",{d:"M4 11h16"}],["path",{d:"M8 15h.01"}],["path",{d:"M16 15h.01"}],["path",{d:"M6 19v2"}],["path",{d:"M18 21v-2"}]],f3=[["path",{d:"M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z"}],["path",{d:"M17 21v-2"}],["path",{d:"M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10"}],["path",{d:"M21 21v-2"}],["path",{d:"M3 5V3"}],["path",{d:"M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z"}],["path",{d:"M7 5V3"}]],k3=[["path",{d:"M8 6v6"}],["path",{d:"M15 6v6"}],["path",{d:"M2 12h19.6"}],["path",{d:"M18 18h3s.5-1.7.8-2.8c.1-.4.2-.8.2-1.2 0-.4-.1-.8-.2-1.2l-1.4-5C20.1 6.8 19.1 6 18 6H4a2 2 0 0 0-2 2v10h3"}],["circle",{cx:"7",cy:"18",r:"2"}],["path",{d:"M9 18h5"}],["circle",{cx:"16",cy:"18",r:"2"}]],P3=[["path",{d:"M10 3h.01"}],["path",{d:"M14 2h.01"}],["path",{d:"m2 9 20-5"}],["path",{d:"M12 12V6.5"}],["rect",{width:"16",height:"10",x:"4",y:"12",rx:"3"}],["path",{d:"M9 12v5"}],["path",{d:"M15 12v5"}],["path",{d:"M4 17h16"}]],B3=[["path",{d:"M16 13H3"}],["path",{d:"M16 17H3"}],["path",{d:"m7.2 7.9-3.388 2.5A2 2 0 0 0 3 12.01V20a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-8.654c0-2-2.44-6.026-6.44-8.026a1 1 0 0 0-1.082.057L10.4 5.6"}],["circle",{cx:"9",cy:"7",r:"2"}]],z3=[["path",{d:"M20 21v-8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8"}],["path",{d:"M4 16s.5-1 2-1 2.5 2 4 2 2.5-2 4-2 2.5 2 4 2 2-1 2-1"}],["path",{d:"M2 21h20"}],["path",{d:"M7 8v3"}],["path",{d:"M12 8v3"}],["path",{d:"M17 8v3"}],["path",{d:"M7 4h.01"}],["path",{d:"M12 4h.01"}],["path",{d:"M17 4h.01"}]],D3=[["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2"}],["line",{x1:"8",x2:"16",y1:"6",y2:"6"}],["line",{x1:"16",x2:"16",y1:"14",y2:"18"}],["path",{d:"M16 10h.01"}],["path",{d:"M12 10h.01"}],["path",{d:"M8 10h.01"}],["path",{d:"M12 14h.01"}],["path",{d:"M8 14h.01"}],["path",{d:"M12 18h.01"}],["path",{d:"M8 18h.01"}]],F3=[["path",{d:"M11 13h1v4"}],["path",{d:"M16 2v3"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],R3=[["path",{d:"m14 17 4 4 4-4"}],["path",{d:"M16 2v3"}],["path",{d:"M18 13v8"}],["path",{d:"M21 10.354V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h7.343"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],b3=[["path",{d:"m14 17 4-4 4 4"}],["path",{d:"M16 2v3"}],["path",{d:"M18 21v-8"}],["path",{d:"M21 10.343V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h9"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],T3=[["path",{d:"M 19 3 L 5 3"}],["path",{d:"M 21 13 L 21 5"}],["path",{d:"M 21 5 A2 2 0 0 0 19 3"}],["path",{d:"M 3 19 A2 2 0 0 0 5 21"}],["path",{d:"M 3 5 L 3 19"}],["path",{d:"M 5 3 A2 2 0 0 0 3 5"}],["path",{d:"m16 19 2 2 4-4"}],["path",{d:"M16 2v3"}],["path",{d:"M3 9h18"}],["path",{d:"M5 21 L12.5 21"}],["path",{d:"M8 2v3"}]],q3=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"m9 15 2 2 4-4"}]],U3=[["path",{d:"m11 21-3-3 3-3"}],["path",{d:"M16 2v3"}],["path",{d:"M21 9H3"}],["path",{d:"M3 11.5V5a2 2 0 012-2h14a2 2 0 012 2v14a2 2 0 01-2 2h-4"}],["path",{d:"m5 21-3-3 3-3"}],["path",{d:"M8 2v3"}]],O3=[["path",{d:"m13 21 3-3-3-3"}],["path",{d:"M16 2v3"}],["path",{d:"m19 21 3-3-3-3"}],["path",{d:"M21 11.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h4"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],G3=[["path",{d:"M16 14v2.2l1.6 1"}],["path",{d:"M16 2v3"}],["path",{d:"M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338"}],["path",{d:"M3 9h5.859"}],["path",{d:"M8 2v3"}],["circle",{cx:"16",cy:"16",r:"6"}]],Z3=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M8 13h.01"}],["path",{d:"M12 13h.01"}],["path",{d:"M16 13h.01"}],["path",{d:"M8 17h.01"}],["path",{d:"M12 17h.01"}],["path",{d:"M16 17h.01"}]],E3=[["path",{d:"m15.228 16.852-.923-.383"}],["path",{d:"m15.228 19.148-.923.383"}],["path",{d:"M16 2v3"}],["path",{d:"m16.47 14.305.382.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"m20.773 16.852.924-.383"}],["path",{d:"m20.773 19.148.924.383"}],["path",{d:"M21 10.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h5.5"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}],["circle",{cx:"18",cy:"18",r:"3"}]],W3=[["path",{d:"M16 2v3"}],["path",{d:"M21 15V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10v-5a1 1 0 011-1za2.4 2.4 0 01-.706 1.706l-3.588 3.588A2.4 2.4 0 0115 21"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],I3=[["path",{d:"M12.127 21H5a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v5.125"}],["path",{d:"M14.62 17.8A2.25 2.25 0 1118 14.836a2.25 2.25 0 113.38 2.966l-2.626 2.856a.998.998 0 01-1.507 0z"}],["path",{d:"M16 2v3"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],X3=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M10 15h4"}]],N3=[["path",{d:"M16 18h6"}],["path",{d:"M16 2v3"}],["path",{d:"M21 14V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h8.3"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],K3=[["path",{d:"M16 2v3"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 9h-5.5"}],["path",{d:"M3 9h6"}],["path",{d:"M3.586 3.586A2 2 0 003 5v14a2 2 0 002 2h14a2 2 0 001.414-.586"}],["path",{d:"M8.656 3H19a2 2 0 012 2v10.344"}]],J3=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M10 15h4"}],["path",{d:"M12 13v4"}]],Q3=[["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M16 2v3"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}],["path",{d:"M17 13h-6"}],["path",{d:"M13 17H7"}],["path",{d:"M7 13h.01"}],["path",{d:"M17 17h.01"}]],j3=[["path",{d:"M16 2v3"}],["path",{d:"M21 10.69V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h7.25"}],["path",{d:"m22 21-1.875-1.875"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}],["circle",{cx:"18",cy:"17",r:"3"}]],Y3=[["path",{d:"M11 10v4h4"}],["path",{d:"m11 14 1.535-1.605a5 5 0 018 1.5"}],["path",{d:"M16 2v3"}],["path",{d:"m21 18-1.535 1.605a5 5 0 01-8-1.5"}],["path",{d:"M21 22v-4h-4"}],["path",{d:"M21 8.517V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h3.517"}],["path",{d:"M3 9h4"}],["path",{d:"M8 2v3"}]],_3=[["path",{d:"M16 18h6"}],["path",{d:"M16 2v3"}],["path",{d:"M19 15v6"}],["path",{d:"M21 11.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h8.3"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],x3=[["path",{d:"M16 2v3"}],["path",{d:"m17 16 5 5"}],["path",{d:"m17 21 5-5"}],["path",{d:"M21 12V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h8"}],["path",{d:"M3 9h18"}],["path",{d:"M8 2v3"}]],ad=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"m14 13-4 4"}],["path",{d:"m10 13 4 4"}]],hd=[["path",{d:"M8 2v3"}],["path",{d:"M16 2v3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M3 9h18"}]],td=[["path",{d:"M12 2v2"}],["path",{d:"M15.726 21.01A2 2 0 0 1 14 22H4a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2"}],["path",{d:"M18 2v2"}],["path",{d:"M2 13h2"}],["path",{d:"M8 8h14"}],["rect",{x:"8",y:"3",width:"14",height:"14",rx:"2"}]],dd=[["path",{d:"M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"}],["circle",{cx:"12",cy:"13",r:"3"}]],cd=[["path",{d:"M14.564 14.558a3 3 0 1 1-4.122-4.121"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20 20H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 .819-.175"}],["path",{d:"M9.695 4.024A2 2 0 0 1 10.004 4h3.993a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v7.344"}]],Md=[["path",{d:"m17 22 1.664-2.496a2 2 0 00.336-1.11V5.606a2 2 0 00-.336-1.11L17 2"}],["path",{d:"M18 22H6"}],["path",{d:"M18 2H6"}],["path",{d:"M5 17h14"}],["path",{d:"M5 7h14"}],["path",{d:"m7 22-1.664-2.496A2 2 0 015 18.394V5.606a2 2 0 01.336-1.11L7 2"}]],pd=[["path",{d:"M21 10.5a9 2.5 0 01-18 0v8a9 2.5 0 0018 0z"}],["path",{d:"M21 10.5A9 2.5 25.32 004.59 3.47 9 2.5 25.32 0021 10.5"}],["path",{d:"M3 10.5a9 2.5 0 016.527-2.405"}],["path",{d:"M9 16.858a31 31 0 006 0"}]],ld=[["path",{d:"m10.8 5 2.111 4.223"}],["path",{d:"M17.75 7 15 2.1"}],["path",{d:"m4.874 14.647 2.12 4.24"}],["path",{d:"M5.7 21a2 2 0 0 1-3.5-2l8.6-14a6 6 0 0 1 10.4 6 2 2 0 1 1-3.464-2 2 2 0 1 0-3.464-2z"}],["path",{d:"m7.906 9.712 2.005 4.411"}]],id=[["path",{d:"M10 10v7.9"}],["path",{d:"M11.802 6.145a5 5 0 0 1 6.053 6.053"}],["path",{d:"M14 6.1v2.243"}],["path",{d:"m15.5 15.571-.964.964a5 5 0 0 1-7.071 0 5 5 0 0 1 0-7.07l.964-.965"}],["path",{d:"M16 7V3a1 1 0 0 1 1.707-.707 2.5 2.5 0 0 0 2.152.717 1 1 0 0 1 1.131 1.131 2.5 2.5 0 0 0 .717 2.152A1 1 0 0 1 21 8h-4"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8 17v4a1 1 0 0 1-1.707.707 2.5 2.5 0 0 0-2.152-.717 1 1 0 0 1-1.131-1.131 2.5 2.5 0 0 0-.717-2.152A1 1 0 0 1 3 16h4"}]],nd=[["path",{d:"M10 7v10.9"}],["path",{d:"M14 6.1V17"}],["path",{d:"M16 7V3a1 1 0 0 1 1.707-.707 2.5 2.5 0 0 0 2.152.717 1 1 0 0 1 1.131 1.131 2.5 2.5 0 0 0 .717 2.152A1 1 0 0 1 21 8h-4"}],["path",{d:"M16.536 7.465a5 5 0 0 0-7.072 0l-2 2a5 5 0 0 0 0 7.07 5 5 0 0 0 7.072 0l2-2a5 5 0 0 0 0-7.07"}],["path",{d:"M8 17v4a1 1 0 0 1-1.707.707 2.5 2.5 0 0 0-2.152-.717 1 1 0 0 1-1.131-1.131 2.5 2.5 0 0 0-.717-2.152A1 1 0 0 1 3 16h4"}]],ed=[["path",{d:"M12 22v-4c1.5 1.5 3.5 3 6 3 0-1.5-.5-3.5-2-5"}],["path",{d:"M13.988 8.327C13.902 6.054 13.365 3.82 12 2a9.3 9.3 0 0 0-1.445 2.9"}],["path",{d:"M17.375 11.725C18.882 10.53 21 7.841 21 6c-2.324 0-5.08 1.296-6.662 2.684"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21.024 15.378A15 15 0 0 0 22 15c-.426-1.279-2.67-2.557-4.25-2.907"}],["path",{d:"M6.995 6.992C5.714 6.4 4.29 6 3 6c0 2 2.5 5 4 6-1.5 0-4.5 1.5-5 3 3.5 1.5 6 1 6 1-1.5 1.5-2 3.5-2 5 2.5 0 4.5-1.5 6-3"}]],rd=[["path",{d:"M12 22v-4"}],["path",{d:"M7 12c-1.5 0-4.5 1.5-5 3 3.5 1.5 6 1 6 1-1.5 1.5-2 3.5-2 5 2.5 0 4.5-1.5 6-3 1.5 1.5 3.5 3 6 3 0-1.5-.5-3.5-2-5 0 0 2.5.5 6-1-.5-1.5-3.5-3-5-3 1.5-1 4-4 4-6-2.5 0-5.5 1.5-7 3 0-2.5-.5-5-2-7-1.5 2-2 4.5-2 7-1.5-1.5-4.5-3-7-3 0 2 2.5 5 4 6"}]],od=[["path",{d:"M10.5 5H19a2 2 0 0 1 2 2v8.5"}],["path",{d:"M17 11h-.5"}],["path",{d:"M19 19H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M7 11h4"}],["path",{d:"M7 15h2.5"}]],T=[["rect",{width:"18",height:"14",x:"3",y:"5",rx:"2",ry:"2"}],["path",{d:"M7 15h4M15 15h2M7 11h2M13 11h4"}]],vd=[["path",{d:"M14 13h4"}],["path",{d:"M16 15v-4"}],["path",{d:"M18 5v2"}],["path",{d:"M6 13h4"}],["path",{d:"M6 5v2"}],["rect",{x:"2",y:"7",width:"20",height:"12",rx:"2"}]],$d=[["path",{d:"m21 8-2 2-1.5-3.7A2 2 0 0 0 15.646 5H8.4a2 2 0 0 0-1.903 1.257L5 10 3 8"}],["path",{d:"M7 14h.01"}],["path",{d:"M17 14h.01"}],["rect",{width:"18",height:"8",x:"3",y:"10",rx:"2"}],["path",{d:"M5 18v2"}],["path",{d:"M19 18v2"}]],md=[["path",{d:"M10 2h4"}],["path",{d:"m21 8-2 2-1.5-3.7A2 2 0 0 0 15.646 5H8.4a2 2 0 0 0-1.903 1.257L5 10 3 8"}],["path",{d:"M7 14h.01"}],["path",{d:"M17 14h.01"}],["rect",{width:"18",height:"8",x:"3",y:"10",rx:"2"}],["path",{d:"M5 18v2"}],["path",{d:"M19 18v2"}]],yd=[["path",{d:"M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2"}],["circle",{cx:"7",cy:"17",r:"2"}],["path",{d:"M9 17h6"}],["circle",{cx:"17",cy:"17",r:"2"}]],sd=[["path",{d:"M18 19V9a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v8a2 2 0 0 0 2 2h2"}],["path",{d:"M2 9h3a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H2"}],["path",{d:"M22 17v1a1 1 0 0 1-1 1H10v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9"}],["circle",{cx:"8",cy:"19",r:"2"}]],Cd=[["path",{d:"M12 14v4"}],["path",{d:"M14.172 2a2 2 0 0 1 1.414.586l3.828 3.828A2 2 0 0 1 20 7.828V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z"}],["path",{d:"M8 14h8"}],["rect",{x:"8",y:"10",width:"8",height:"8",rx:"2"}]],ud=[["path",{d:"M15 16a1 1 0 0 0-7-7q-4 4-5.987 12.385a.5.5 0 0 0 .602.602Q11 20 15 16l-3-3"}],["path",{d:"M15 9q4 4 7 0-3-4-7 0 4-4 0-7-4 3 0 7"}],["path",{d:"m8 15-2.58-2.58"}]],gd=[["path",{d:"M10 10H5v10a2 2 0 002 2h10a2 2 0 002-2v-1"}],["path",{d:"M13 22v-9"}],["path",{d:"M13.902 8.245 16 6h-4.343"}],["path",{d:"M19 13.343V10a2 2 0 00-.539-1.367L16 6V3a1 1 0 00-1-1H9a1 1 0 00-.857.486"}],["path",{d:"m2 2 20 20"}],["path",{d:"M7.034 7.034 5.539 8.633A2 2 0 005 10"}]],Ad=[["path",{d:"M13 22V10a2 2 0 01.539-1.367L16 6H8L5.539 8.633A2 2 0 005 10v10a2 2 0 002 2h10a2 2 0 002-2V10a2 2 0 00-.539-1.367L16 6V3a1 1 0 00-1-1H9a1 1 0 00-1 1v3"}],["path",{d:"M5 10h8"}]],Hd=[["path",{d:"M10 9v7"}],["path",{d:"M14 6v10"}],["circle",{cx:"17.5",cy:"12.5",r:"3.5"}],["circle",{cx:"6.5",cy:"12.5",r:"3.5"}]],Vd=[["path",{d:"m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16"}],["path",{d:"M22 9v7"}],["path",{d:"M3.304 13h6.392"}],["circle",{cx:"18.5",cy:"12.5",r:"3.5"}]],Sd=[["path",{d:"M15 11h4.5a1 1 0 0 1 0 5h-4a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h3a1 1 0 0 1 0 5"}],["path",{d:"m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16"}],["path",{d:"M3.304 13h6.392"}]],wd=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["circle",{cx:"8",cy:"10",r:"2"}],["path",{d:"M8 12h8"}],["circle",{cx:"16",cy:"10",r:"2"}],["path",{d:"m6 20 .7-2.9A1.4 1.4 0 0 1 8.1 16h7.8a1.4 1.4 0 0 1 1.4 1l.7 3"}]],Ld=[["path",{d:"M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"}],["path",{d:"M2 12a9 9 0 0 1 8 8"}],["path",{d:"M2 16a5 5 0 0 1 4 4"}],["line",{x1:"2",x2:"2.01",y1:"20",y2:"20"}]],fd=[["path",{d:"M10 5V3"}],["path",{d:"M14 5V3"}],["path",{d:"M15 21v-3a3 3 0 0 0-6 0v3"}],["path",{d:"M18 3v8"}],["path",{d:"M18 5H6"}],["path",{d:"M22 11H2"}],["path",{d:"M22 9v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9"}],["path",{d:"M6 3v8"}]],kd=[["path",{d:"M12 5c.67 0 1.35.09 2 .26 1.78-2 5.03-2.84 6.42-2.26 1.4.58-.42 7-.42 7 .57 1.07 1 2.24 1 3.44C21 17.9 16.97 21 12 21s-9-3-9-7.56c0-1.25.5-2.4 1-3.44 0 0-1.89-6.42-.5-7 1.39-.58 4.72.23 6.5 2.23A9.04 9.04 0 0 1 12 5Z"}],["path",{d:"M8 14v.5"}],["path",{d:"M16 14v.5"}],["path",{d:"M11.25 16.25h1.5L12 17l-.75-.75Z"}]],Pd=[["path",{d:"m12.309 6.652 4.797 2.401a1 1 0 0 1 .447 1.341l-.501 1.001.605.605h2.725a1 1 0 0 1 .894 1.447l-.724 1.448"}],["path",{d:"m15.166 15.166-.719 1.439a1 1 0 0 1-1.342.447L3.61 12.3a2.92 2.92 0 0 1-1.3-3.91L3.69 5.6a2.9 2.9 0 0 1 .873-1.037"}],["path",{d:"M2 19h3.76a2 2 0 0 0 1.8-1.1l1.441-2.902"}],["path",{d:"m2 2 20 20"}],["path",{d:"M2 21v-4"}],["path",{d:"M7 9h.01"}]],Bd=[["path",{d:"M16.75 12h3.632a1 1 0 0 1 .894 1.447l-2.034 4.069a1 1 0 0 1-1.708.134l-2.124-2.97"}],["path",{d:"M17.106 9.053a1 1 0 0 1 .447 1.341l-3.106 6.211a1 1 0 0 1-1.342.447L3.61 12.3a2.92 2.92 0 0 1-1.3-3.91L3.69 5.6a2.92 2.92 0 0 1 3.92-1.3z"}],["path",{d:"M2 19h3.76a2 2 0 0 0 1.8-1.1L9 15"}],["path",{d:"M2 21v-4"}],["path",{d:"M7 9h.01"}]],zd=[["path",{d:"M12 10H6"}],["path",{d:"m20.69 15.479 1.29 4.88a.5.5 0 01-.698.591l-1.843-.849a1 1 0 00-.879.001l-1.846.85a.5.5 0 01-.692-.593l1.29-4.88"}],["path",{d:"M22 6.675V6a2 2 0 00-2-2H4a2 2 0 00-2 2v12a2 2 0 002 2h8"}],["path",{d:"M6 14h4"}],["circle",{cx:"19",cy:"13",r:"3"}]],q=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z"}]],U=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["rect",{x:"7",y:"13",width:"9",height:"4",rx:"1"}],["rect",{x:"7",y:"5",width:"12",height:"4",rx:"1"}]],Dd=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M7 11h8"}],["path",{d:"M7 16h12"}],["path",{d:"M7 6h3"}]],Fd=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M7 11h8"}],["path",{d:"M7 16h3"}],["path",{d:"M7 6h12"}]],Rd=[["path",{d:"M11 13v4"}],["path",{d:"M15 5v4"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["rect",{x:"7",y:"13",width:"9",height:"4",rx:"1"}],["rect",{x:"7",y:"5",width:"12",height:"4",rx:"1"}]],O=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M7 16h8"}],["path",{d:"M7 11h12"}],["path",{d:"M7 6h3"}]],G=[["path",{d:"M9 5v4"}],["rect",{width:"4",height:"6",x:"7",y:"9",rx:"1"}],["path",{d:"M9 15v2"}],["path",{d:"M17 3v2"}],["rect",{width:"4",height:"8",x:"15",y:"5",rx:"1"}],["path",{d:"M17 13v3"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}]],Z=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["rect",{x:"15",y:"5",width:"4",height:"12",rx:"1"}],["rect",{x:"7",y:"8",width:"4",height:"9",rx:"1"}]],bd=[["path",{d:"M13 17V9"}],["path",{d:"M18 17v-3"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M8 17V5"}]],E=[["path",{d:"M13 17V9"}],["path",{d:"M18 17V5"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M8 17v-3"}]],Td=[["path",{d:"M11 13H7"}],["path",{d:"M19 9h-4"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["rect",{x:"15",y:"5",width:"4",height:"12",rx:"1"}],["rect",{x:"7",y:"8",width:"4",height:"9",rx:"1"}]],W=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M18 17V9"}],["path",{d:"M13 17V5"}],["path",{d:"M8 17v-3"}]],qd=[["path",{d:"M10 6h8"}],["path",{d:"M12 16h6"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M8 11h7"}]],I=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"m19 9-5 5-4-4-3 3"}]],Ud=[["path",{d:"m13.11 7.664 1.78 2.672"}],["path",{d:"m14.162 12.788-3.324 1.424"}],["path",{d:"m20 4-6.06 1.515"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["circle",{cx:"12",cy:"6",r:"2"}],["circle",{cx:"16",cy:"12",r:"2"}],["circle",{cx:"9",cy:"15",r:"2"}]],X=[["path",{d:"M5 21v-6"}],["path",{d:"M12 21V9"}],["path",{d:"M19 21V3"}]],Od=[["path",{d:"M5 21V3"}],["path",{d:"M12 21V9"}],["path",{d:"M19 21v-6"}]],N=[["path",{d:"M5 21v-6"}],["path",{d:"M12 21V3"}],["path",{d:"M19 21V9"}]],Gd=[["path",{d:"M12 16v5"}],["path",{d:"M16 14.639V21"}],["path",{d:"M20 10.656V21"}],["path",{d:"m22 3-8.646 8.646a.5.5 0 0 1-.708 0L9.354 8.354a.5.5 0 0 0-.707 0L2 15"}],["path",{d:"M4 18.463V21"}],["path",{d:"M8 14.656V21"}]],K=[["path",{d:"M6 5h12"}],["path",{d:"M4 12h10"}],["path",{d:"M12 19h8"}]],J=[["path",{d:"M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z"}],["path",{d:"M21.21 15.89A10 10 0 1 1 8 2.83"}]],Q=[["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor"}],["circle",{cx:"18.5",cy:"5.5",r:".5",fill:"currentColor"}],["circle",{cx:"11.5",cy:"11.5",r:".5",fill:"currentColor"}],["circle",{cx:"7.5",cy:"16.5",r:".5",fill:"currentColor"}],["circle",{cx:"17.5",cy:"14.5",r:".5",fill:"currentColor"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}]],Zd=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16"}],["path",{d:"M7 16c.5-2 1.5-7 4-7 2 0 2 3 4 3 2.5 0 4.5-5 5-7"}]],Ed=[["path",{d:"M18 6 7 17l-5-5"}],["path",{d:"m22 10-7.5 7.5L13 16"}]],Wd=[["path",{d:"M20 4L9 15"}],["path",{d:"M21 19L3 19"}],["path",{d:"M9 15L4 10"}]],Id=[["path",{d:"M20 6 9 17l-5-5"}]],Xd=[["path",{d:"M17 21a1 1 0 0 0 1-1v-5.35c0-.457.316-.844.727-1.041a4 4 0 0 0-2.134-7.589 5 5 0 0 0-9.186 0 4 4 0 0 0-2.134 7.588c.411.198.727.585.727 1.041V20a1 1 0 0 0 1 1Z"}],["path",{d:"M6 17h12"}]],Nd=[["path",{d:"M2 17a5 5 0 0 0 10 0c0-2.76-2.5-5-5-3-2.5-2-5 .24-5 3Z"}],["path",{d:"M12 17a5 5 0 0 0 10 0c0-2.76-2.5-5-5-3-2.5-2-5 .24-5 3Z"}],["path",{d:"M7 14c3.22-2.91 4.29-8.75 5-12 1.66 2.38 4.94 9 5 12"}],["path",{d:"M22 9c-4.29 0-7.14-2.33-10-7 5.71 0 10 4.67 10 7Z"}]],Kd=[["path",{d:"M4 20a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1z"}],["path",{d:"m6.7 18-1-1C4.35 15.682 3 14.09 3 12a5 5 0 0 1 4.95-5c1.584 0 2.7.455 4.05 1.818C13.35 7.455 14.466 7 16.05 7A5 5 0 0 1 21 12c0 2.082-1.359 3.673-2.7 5l-1 1"}],["path",{d:"M10 4h4"}],["path",{d:"M12 2v6.818"}]],Jd=[["path",{d:"M5 20a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z"}],["path",{d:"M15 18c1.5-.615 3-2.461 3-4.923C18 8.769 14.5 4.462 12 2 9.5 4.462 6 8.77 6 13.077 6 15.539 7.5 17.385 9 18"}],["path",{d:"m16 7-2.5 2.5"}],["path",{d:"M9 2h6"}]],Qd=[["path",{d:"M5 20a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z"}],["path",{d:"M16.5 18c1-2 2.5-5 2.5-9a7 7 0 0 0-7-7H6.635a1 1 0 0 0-.768 1.64L7 5l-2.32 5.802a2 2 0 0 0 .95 2.526l2.87 1.456"}],["path",{d:"m15 5 1.425-1.425"}],["path",{d:"m17 8 1.53-1.53"}],["path",{d:"M9.713 12.185 7 18"}]],jd=[["path",{d:"M5 20a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z"}],["path",{d:"m14.5 10 1.5 8"}],["path",{d:"M7 10h10"}],["path",{d:"m8 18 1.5-8"}],["circle",{cx:"12",cy:"6",r:"4"}]],Yd=[["path",{d:"m10.68 5.499-.996 4.982a.65.65 0 01-1.131.295L5.885 7.663"}],["path",{d:"m13.32 5.5.995 4.98a.65.65 0 001.131.296l2.668-3.113"}],["path",{d:"M20.428 8.952 17.5 18"}],["path",{d:"M4 20a2 2 0 012-2h12a2 2 0 012 2v1a1 1 0 01-1 1H5a1 1 0 01-1-1z"}],["path",{d:"M6.5 18 3.571 8.952"}],["circle",{cx:"12",cy:"4",r:"2"}],["circle",{cx:"20",cy:"7",r:"2"}],["circle",{cx:"4",cy:"7",r:"2"}]],_d=[["path",{d:"M5 20a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z"}],["path",{d:"M10 2v2"}],["path",{d:"M14 2v2"}],["path",{d:"m17 18-1-9"}],["path",{d:"M6 2v5a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2"}],["path",{d:"M6 4h12"}],["path",{d:"m7 18 1-9"}]],xd=[["path",{d:"m6 9 6 6 6-6"}]],a6=[["path",{d:"m17 18-6-6 6-6"}],["path",{d:"M7 6v12"}]],h6=[["path",{d:"m7 18 6-6-6-6"}],["path",{d:"M17 6v12"}]],t6=[["path",{d:"m15 18-6-6 6-6"}]],d6=[["path",{d:"m9 18 6-6-6-6"}]],c6=[["path",{d:"m18 15-6-6-6 6"}]],M6=[["path",{d:"m7 20 5-5 5 5"}],["path",{d:"m7 4 5 5 5-5"}]],p6=[["path",{d:"m7 6 5 5 5-5"}],["path",{d:"m7 13 5 5 5-5"}]],l6=[["path",{d:"M12 12h.01"}],["path",{d:"M16 12h.01"}],["path",{d:"m17 7 5 5-5 5"}],["path",{d:"m7 7-5 5 5 5"}],["path",{d:"M8 12h.01"}]],i6=[["path",{d:"m9 7-5 5 5 5"}],["path",{d:"m15 7 5 5-5 5"}]],n6=[["path",{d:"m11 17-5-5 5-5"}],["path",{d:"m18 17-5-5 5-5"}]],e6=[["path",{d:"m20 17-5-5 5-5"}],["path",{d:"m4 17 5-5-5-5"}]],r6=[["path",{d:"m6 17 5-5-5-5"}],["path",{d:"m13 17 5-5-5-5"}]],o6=[["path",{d:"m7 15 5 5 5-5"}],["path",{d:"m7 9 5-5 5 5"}]],v6=[["path",{d:"M10 9h4"}],["path",{d:"M12 7v5"}],["path",{d:"M14 21v-3a2 2 0 0 0-4 0v3"}],["path",{d:"m18 9 3.52 2.147a1 1 0 0 1 .48.854V19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-6.999a1 1 0 0 1 .48-.854L6 9"}],["path",{d:"M6 21V7a1 1 0 0 1 .376-.782l5-3.999a1 1 0 0 1 1.249.001l5 4A1 1 0 0 1 18 7v14"}]],$6=[["path",{d:"m17 11-5-5-5 5"}],["path",{d:"m17 18-5-5-5 5"}]],m6=[["path",{d:"M12 12H3a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h13"}],["path",{d:"M18 8c0-2.5-2-2.5-2-5"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 12a1 1 0 0 1 1 1v2a1 1 0 0 1-.5.866"}],["path",{d:"M22 8c0-2.5-2-2.5-2-5"}],["path",{d:"M7 12v4"}]],y6=[["path",{d:"M17 12H3a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h14"}],["path",{d:"M18 8c0-2.5-2-2.5-2-5"}],["path",{d:"M21 16a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"}],["path",{d:"M22 8c0-2.5-2-2.5-2-5"}],["path",{d:"M7 12v4"}]],j=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]],Y=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m12 8-4 4 4 4"}],["path",{d:"M16 12H8"}]],_=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 8v8"}],["path",{d:"m8 12 4 4 4-4"}]],x=[["path",{d:"M2 12a10 10 0 1 1 10 10"}],["path",{d:"m2 22 10-10"}],["path",{d:"M8 22H2v-6"}]],a1=[["path",{d:"M12 22a10 10 0 1 1 10-10"}],["path",{d:"M22 22 12 12"}],["path",{d:"M22 16v6h-6"}]],h1=[["path",{d:"M2 8V2h6"}],["path",{d:"m2 2 10 10"}],["path",{d:"M12 2A10 10 0 1 1 2 12"}]],t1=[["path",{d:"M22 12A10 10 0 1 1 12 2"}],["path",{d:"M22 2 12 12"}],["path",{d:"M16 2h6v6"}]],d1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m12 16 4-4-4-4"}],["path",{d:"M8 12h8"}]],c1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m16 12-4-4-4 4"}],["path",{d:"M12 16V8"}]],M1=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335"}],["path",{d:"m9 11 3 3L22 4"}]],p1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m16 9-5.5 5.5L8 12"}]],l1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m16 10-4 4-4-4"}]],i1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m14 16-4-4 4-4"}]],n1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m10 8 4 4-4 4"}]],e1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m8 14 4-4 4 4"}]],s6=[["path",{d:"M9 14L12 17L15 14"}],["path",{d:"M9 8L12 11L15 8"}],["circle",{cx:"12",cy:"12",r:"10"}]],C6=[["path",{d:"M10 9L7 12L10 15"}],["path",{d:"M16 9L13 12L16 15"}],["circle",{cx:"12",cy:"12",r:"10"}]],u6=[["path",{d:"M14 15L17 12L14 9"}],["path",{d:"M8 15L11 12L8 9"}],["circle",{cx:"12",cy:"12",r:"10"}]],g6=[["path",{d:"M15 10L12 7L9 10"}],["path",{d:"M15 16L12 13L9 16"}],["circle",{cx:"12",cy:"12",r:"10"}]],A6=[["path",{d:"M10.1 2.182a10 10 0 013.8 0"}],["path",{d:"M13.9 21.818a10 10 0 01-3.8 0"}],["path",{d:"m16 9-5.5 5.5L8 12"}],["path",{d:"M17.609 3.721a10 10 0 012.69 2.7"}],["path",{d:"M2.182 13.9a10 10 0 010-3.8"}],["path",{d:"M20.279 17.609a10 10 0 01-2.7 2.69"}],["path",{d:"M21.818 10.1a10 10 0 010 3.8"}],["path",{d:"M3.721 6.391a10 10 0 012.7-2.69"}],["path",{d:"M6.391 20.279a10 10 0 01-2.69-2.7"}]],H6=[["path",{d:"M10.1 2.182a10 10 0 0 1 3.8 0"}],["path",{d:"M13.9 21.818a10 10 0 0 1-3.8 0"}],["path",{d:"M17.609 3.721a10 10 0 0 1 2.69 2.7"}],["path",{d:"M2.182 13.9a10 10 0 0 1 0-3.8"}],["path",{d:"M20.279 17.609a10 10 0 0 1-2.7 2.69"}],["path",{d:"M21.818 10.1a10 10 0 0 1 0 3.8"}],["path",{d:"M3.721 6.391a10 10 0 0 1 2.7-2.69"}],["path",{d:"M6.391 20.279a10 10 0 0 1-2.69-2.7"}]],r1=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12"}],["line",{x1:"12",x2:"12",y1:"16",y2:"16"}],["line",{x1:"12",x2:"12",y1:"8",y2:"8"}]],V6=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"}],["path",{d:"M12 18V6"}]],S6=[["path",{d:"M10.1 2.18a9.93 9.93 0 0 1 3.8 0"}],["path",{d:"M17.6 3.71a9.95 9.95 0 0 1 2.69 2.7"}],["path",{d:"M21.82 10.1a9.93 9.93 0 0 1 0 3.8"}],["path",{d:"M20.29 17.6a9.95 9.95 0 0 1-2.7 2.69"}],["path",{d:"M13.9 21.82a9.94 9.94 0 0 1-3.8 0"}],["path",{d:"M6.4 20.29a9.95 9.95 0 0 1-2.69-2.7"}],["path",{d:"M2.18 13.9a9.93 9.93 0 0 1 0-3.8"}],["path",{d:"M3.71 6.4a9.95 9.95 0 0 1 2.7-2.69"}],["circle",{cx:"12",cy:"12",r:"1"}]],w6=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"12",cy:"12",r:"10"}]],L6=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M17 12h.01"}],["path",{d:"M12 12h.01"}],["path",{d:"M7 12h.01"}]],f6=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M7 10h10"}],["path",{d:"M7 14h10"}]],k6=[["path",{d:"M15 9.4a4 4 0 1 0 0 5.2"}],["path",{d:"M7 12h5"}],["circle",{cx:"12",cy:"12",r:"10"}]],P6=[["path",{d:"M12 2a10 10 0 0 1 7.38 16.75"}],["path",{d:"m16 12-4-4-4 4"}],["path",{d:"M12 16V8"}],["path",{d:"M2.5 8.875a10 10 0 0 0-.5 3"}],["path",{d:"M2.83 16a10 10 0 0 0 2.43 3.4"}],["path",{d:"M4.636 5.235a10 10 0 0 1 .891-.857"}],["path",{d:"M8.644 21.42a10 10 0 0 0 7.631-.38"}]],B6=[["path",{d:"M12 2a10 10 0 0 1 7.38 16.75"}],["path",{d:"M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z"}],["path",{d:"M2.5 8.875a10 10 0 0 0-.5 3"}],["path",{d:"M2.83 16a10 10 0 0 0 2.43 3.4"}],["path",{d:"M4.636 5.235a10 10 0 0 1 .891-.857"}],["path",{d:"M8.644 21.42a10 10 0 0 0 7.631-.38"}]],z6=[["path",{d:"M12 2a10 10 0 0 1 7.38 16.75"}],["path",{d:"M12 8v8"}],["path",{d:"M16 12H8"}],["path",{d:"M2.5 8.875a10 10 0 0 0-.5 3"}],["path",{d:"M2.83 16a10 10 0 0 0 2.43 3.4"}],["path",{d:"M4.636 5.235a10 10 0 0 1 .891-.857"}],["path",{d:"M8.644 21.42a10 10 0 0 0 7.631-.38"}]],o1=[["path",{d:"M15.6 2.7a10 10 0 1 0 5.7 5.7"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M13.4 10.6 19 5"}]],v1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M8 12h8"}]],D6=[["path",{d:"m2 2 20 20"}],["path",{d:"M8.35 2.69A10 10 0 0 1 21.3 15.65"}],["path",{d:"M19.08 19.08A10 10 0 1 1 4.92 4.92"}]],$1=[["path",{d:"M12.656 7H13a3 3 0 0 1 2.984 3.307"}],["path",{d:"M13 13H9"}],["path",{d:"M19.071 19.071A1 1 0 0 1 4.93 4.93"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.357 2.687a10 10 0 0 1 12.956 12.956"}],["path",{d:"M9 17V9"}]],m1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M9 17V7h4a3 3 0 0 1 0 6H9"}]],y1=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"10",x2:"10",y1:"15",y2:"9"}],["line",{x1:"14",x2:"14",y1:"15",y2:"9"}]],s1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"M9 9h.01"}],["path",{d:"M15 15h.01"}]],F6=[["circle",{cx:"12",cy:"19",r:"2"}],["circle",{cx:"12",cy:"5",r:"2"}],["circle",{cx:"16",cy:"12",r:"2"}],["circle",{cx:"20",cy:"19",r:"2"}],["circle",{cx:"4",cy:"19",r:"2"}],["circle",{cx:"8",cy:"12",r:"2"}]],C1=[["path",{d:"M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z"}],["circle",{cx:"12",cy:"12",r:"10"}]],u1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M8 12h8"}],["path",{d:"M12 8v8"}]],g1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 7v4"}],["path",{d:"M7.998 9.003a5 5 0 1 0 8-.005"}]],R6=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M10 16V9.5a1 1 0 0 1 5 0"}],["path",{d:"M8 12h4"}],["path",{d:"M8 16h7"}]],n=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]],A1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M22 2 2 22"}]],b6=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"9",x2:"15",y1:"15",y2:"9"}]],T6=[["circle",{cx:"12",cy:"12",r:"6"}]],q6=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M11.051 7.616a1 1 0 0 1 1.909.024l.737 1.452a1 1 0 0 0 .737.535l1.634.256a1 1 0 0 1 .588 1.806l-1.172 1.168a1 1 0 0 0-.282.866l.259 1.613a1 1 0 0 1-1.541 1.134l-1.465-.75a1 1 0 0 0-.912 0l-1.465.75a1 1 0 0 1-1.539-1.133l.258-1.613a1 1 0 0 0-.282-.867l-1.156-1.152a1 1 0 0 1 .572-1.822l1.633-.256a1 1 0 0 0 .737-.535z"}]],H1=[["circle",{cx:"12",cy:"12",r:"10"}],["rect",{x:"9",y:"9",width:"6",height:"6",rx:"1"}]],V1=[["path",{d:"M17.925 20.056a6 6 0 0 0-11.851.001"}],["circle",{cx:"12",cy:"11",r:"4"}],["circle",{cx:"12",cy:"12",r:"10"}]],S1=[["circle",{cx:"12",cy:"12",r:"10"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662"}]],w1=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]],U6=[["circle",{cx:"12",cy:"12",r:"10"}]],O6=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M11 9h4a2 2 0 0 0 2-2V3"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"M7 21v-4a2 2 0 0 1 2-2h4"}],["circle",{cx:"15",cy:"15",r:"2"}]],G6=[["path",{d:"M21.66 17.67a1.08 1.08 0 0 1-.04 1.6A12 12 0 0 1 4.73 2.38a1.1 1.1 0 0 1 1.61-.04z"}],["path",{d:"M19.65 15.66A8 8 0 0 1 8.35 4.34"}],["path",{d:"m14 10-5.5 5.5"}],["path",{d:"M14 17.85V10H6.15"}]],Z6=[["path",{d:"m12.296 3.464 3.02 3.956"}],["path",{d:"M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3z"}],["path",{d:"M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"}],["path",{d:"m6.18 5.276 3.1 3.899"}]],E6=[["path",{d:"M10 4v16"}],["path",{d:"M14 4.764a3 3 0 1 1-.152 4.327A4 4 0 0 1 10 12a4 4 0 0 1 3.848 2.909A3 3 0 1 1 14 19.236"}],["path",{d:"M6 4v16"}]],W6=[["path",{d:"M19 11h.01"}],["path",{d:"M19 6h.01"}],["path",{d:"M5 8c0-4 4-4 4-4 6 0 6 6 6 6 0 7-10 11-10 11"}],["circle",{cx:"7",cy:"8",r:"2"}]],I6=[["path",{d:"M10.586 21.414a2 2 0 0 0 3.378-1.791L11.036 4.377a2 2 0 1 1 3.378 1.037C12.414 7.414 7 8 7 13a5 5 0 0 0 5 5 5 4 0 0 0 5-4 3 3 0 0 0-3-3 3 2 0 0 0-3 2"}]],X6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"m9 14 2 2 4-4"}]],N6=[["path",{d:"M16 14v2.2l1.6 1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v.832"}],["path",{d:"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h2"}],["circle",{cx:"16",cy:"16",r:"6"}],["rect",{x:"8",y:"2",width:"8",height:"4",rx:"1"}]],K6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v4"}],["path",{d:"M21 14H11"}],["path",{d:"m15 10-4 4 4 4"}]],J6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"M12 11h4"}],["path",{d:"M12 16h4"}],["path",{d:"M8 11h.01"}],["path",{d:"M8 16h.01"}]],Q6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"M9 14h6"}]],j6=[["path",{d:"M11 14h10"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v1.344"}],["path",{d:"m17 18 4-4-4-4"}],["path",{d:"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113"}],["rect",{x:"8",y:"2",width:"8",height:"4",rx:"1"}]],L1=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1"}],["path",{d:"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-.5"}],["path",{d:"M16 4h2a2 2 0 0 1 1.73 1"}],["path",{d:"M8 18h1"}],["path",{d:"M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}]],f1=[["path",{d:"M16 4h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21.34 15.664a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}],["path",{d:"M8 22H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["rect",{x:"8",y:"2",width:"8",height:"4",rx:"1"}]],Y6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"M9 14h6"}],["path",{d:"M12 17v-6"}]],_6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"M9 12v-1h6v1"}],["path",{d:"M11 17h2"}],["path",{d:"M12 11v6"}]],x6=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}],["path",{d:"m14.5 11.5-5 5"}],["path",{d:"m9.5 11.5 5 5"}]],a8=[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"}]],h8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l2-4"}]],t8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l-4-2"}]],d8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l-2-4"}]],c8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6"}]],M8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l4-2"}]],p8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6h4"}]],l8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l4 2"}]],i8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l2 4"}]],n8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v10"}]],e8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l-2 4"}]],r8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l-4 2"}]],o8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6H8"}]],v8=[["path",{d:"M12 6v6l4 2"}],["path",{d:"M20 12v5"}],["path",{d:"M20 21h.01"}],["path",{d:"M21.25 8.2A10 10 0 1 0 16 21.16"}]],$8=[["path",{d:"M12 6v6l2 1"}],["path",{d:"M12.337 21.994a10 10 0 1 1 9.588-8.767"}],["path",{d:"m14 18 4 4 4-4"}],["path",{d:"M18 14v8"}]],m8=[["path",{d:"M12 6v6l1.5.8"}],["path",{d:"M12.338 21.994a10 10 0 1 1 9.587-8.767"}],["path",{d:"M14 18h8"}],["path",{d:"m18 22-4-4 4-4"}]],y8=[["path",{d:"M12 6v6l2 1"}],["path",{d:"M13.5 21.885A10 10 0 1 1 22 12"}],["path",{d:"M14 18h8"}],["path",{d:"m18 22 4-4-4-4"}]],s8=[["path",{d:"M12 6v6l1.56.78"}],["path",{d:"M13.227 21.925a10 10 0 1 1 8.767-9.588"}],["path",{d:"m14 18 4-4 4 4"}],["path",{d:"M18 22v-8"}]],C8=[["path",{d:"M21.95 13a10 10 0 1 0-8.685 8.92"}],["path",{d:"M12 6v6l4 2"}],["path",{d:"m16 19 2 2 4-4"}]],u8=[["path",{d:"M12 2a10 10 0 0 1 7.38 16.75"}],["path",{d:"M12 6v6l4 2"}],["path",{d:"M2.5 8.875a10 10 0 0 0-.5 3"}],["path",{d:"M2.83 16a10 10 0 0 0 2.43 3.4"}],["path",{d:"M4.636 5.235a10 10 0 0 1 .891-.857"}],["path",{d:"M8.644 21.42a10 10 0 0 0 7.631-.38"}]],g8=[["path",{d:"M12 6v6l3.644 1.822"}],["path",{d:"M16 19h6"}],["path",{d:"M19 16v6"}],["path",{d:"M21.92 13.267a10 10 0 1 0-8.653 8.653"}]],A8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6l4 2"}]],H8=[["path",{d:"M10 9.17a3 3 0 1 0 0 5.66"}],["path",{d:"M17 9.17a3 3 0 1 0 0 5.66"}],["rect",{x:"2",y:"5",width:"20",height:"14",rx:"2"}]],V8=[["path",{d:"M12 12v4"}],["path",{d:"M12 20h.01"}],["path",{d:"M8.128 16.949A7 7 0 1 1 15.71 8h1.79a1 1 0 0 1 0 9h-1.642"}]],S8=[["path",{d:"M21 15.251A4.5 4.5 0 0 0 17.5 8h-1.79A7 7 0 1 0 3 13.607"}],["path",{d:"M7 11v4h4"}],["path",{d:"M8 19a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5 4.82 4.82 0 0 0-3.41 1.41L7 15"}]],w8=[["path",{d:"m17 15-5.5 5.5L9 18"}],["path",{d:"M5.516 16.07A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 3.501 7.327"}]],L8=[["path",{d:"m10.852 19.772-.383.924"}],["path",{d:"m13.148 14.228.383-.923"}],["path",{d:"M13.148 19.772a3 3 0 1 0-2.296-5.544l-.383-.923"}],["path",{d:"m13.53 20.696-.382-.924a3 3 0 1 1-2.296-5.544"}],["path",{d:"m14.772 15.852.923-.383"}],["path",{d:"m14.772 18.148.923.383"}],["path",{d:"M4.2 15.1a7 7 0 1 1 9.93-9.858A7 7 0 0 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.2"}],["path",{d:"m9.228 15.852-.923-.383"}],["path",{d:"m9.228 18.148-.923.383"}]],k1=[["path",{d:"M12 13v8l-4-4"}],["path",{d:"m12 21 4-4"}],["path",{d:"M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284"}]],f8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"M8 19v1"}],["path",{d:"M8 14v1"}],["path",{d:"M16 19v1"}],["path",{d:"M16 14v1"}],["path",{d:"M12 21v1"}],["path",{d:"M12 16v1"}]],k8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"M16 17H7"}],["path",{d:"M17 21H9"}]],P8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"M16 14v2"}],["path",{d:"M8 14v2"}],["path",{d:"M16 20h.01"}],["path",{d:"M8 20h.01"}],["path",{d:"M12 16v2"}],["path",{d:"M12 22h.01"}]],B8=[["path",{d:"M11 20v2"}],["path",{d:"M18.376 14.512a6 6 0 0 0 3.461-4.127c.148-.625-.659-.97-1.248-.714a4 4 0 0 1-5.259-5.26c.255-.589-.09-1.395-.716-1.248a6 6 0 0 0-4.594 5.36"}],["path",{d:"M3 20a5 5 0 1 1 8.9-4H13a3 3 0 0 1 2 5.24"}],["path",{d:"M7 19v2"}]],z8=[["path",{d:"M6 16.326A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 .5 8.973"}],["path",{d:"m13 12-3 5h4l-3 5"}]],D8=[["path",{d:"M13 16a3 3 0 0 1 0 6H7a5 5 0 1 1 4.9-6z"}],["path",{d:"M18.376 14.512a6 6 0 0 0 3.461-4.127c.148-.625-.659-.97-1.248-.714a4 4 0 0 1-5.259-5.26c.255-.589-.09-1.395-.716-1.248a6 6 0 0 0-4.594 5.36"}]],F8=[["path",{d:"M10.94 5.274A7 7 0 0 1 15.71 10h1.79a4.5 4.5 0 0 1 4.222 6.057"}],["path",{d:"M18.796 18.81A4.5 4.5 0 0 1 17.5 19H9A7 7 0 0 1 5.79 5.78"}],["path",{d:"m2 2 20 20"}]],R8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"m9.2 22 3-7"}],["path",{d:"m9 13-3 7"}],["path",{d:"m17 13-3 7"}]],b8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"M16 14v6"}],["path",{d:"M8 14v6"}],["path",{d:"M12 16v6"}]],T8=[["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"M8 15h.01"}],["path",{d:"M8 19h.01"}],["path",{d:"M12 17h.01"}],["path",{d:"M12 21h.01"}],["path",{d:"M16 15h.01"}],["path",{d:"M16 19h.01"}]],q8=[["path",{d:"M12 2v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"M20 12h2"}],["path",{d:"m19.07 4.93-1.41 1.41"}],["path",{d:"M15.947 12.65a4 4 0 0 0-5.925-4.128"}],["path",{d:"M13 22H7a5 5 0 1 1 4.9-6H13a3 3 0 0 1 0 6Z"}]],U8=[["path",{d:"M12 2v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"M20 12h2"}],["path",{d:"m19.07 4.93-1.41 1.41"}],["path",{d:"M15.947 12.65a4 4 0 0 0-5.925-4.128"}],["path",{d:"M3 20a5 5 0 1 1 8.9-4H13a3 3 0 0 1 2 5.24"}],["path",{d:"M11 20v2"}],["path",{d:"M7 19v2"}]],O8=[["path",{d:"m17 18-1.535 1.605a5 5 0 0 1-8-1.5"}],["path",{d:"M17 22v-4h-4"}],["path",{d:"M20.996 15.251A4.5 4.5 0 0 0 17.495 8h-1.79a7 7 0 1 0-12.709 5.607"}],["path",{d:"M7 10v4h4"}],["path",{d:"m7 14 1.535-1.605a5 5 0 0 1 8 1.5"}]],P1=[["path",{d:"M12 13v8"}],["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"}],["path",{d:"m8 17 4-4 4 4"}]],G8=[["path",{d:"M17.5 12a1 1 0 1 1 0 9H9.006a7 7 0 1 1 6.702-9z"}],["path",{d:"M21.832 9A3 3 0 0 0 19 7h-2.207a5.5 5.5 0 0 0-10.72.61"}]],Z8=[["path",{d:"M16.17 7.83 2 22"}],["path",{d:"M4.02 12a2.827 2.827 0 1 1 3.81-4.17A2.827 2.827 0 1 1 12 4.02a2.827 2.827 0 1 1 4.17 3.81A2.827 2.827 0 1 1 19.98 12a2.827 2.827 0 1 1-3.81 4.17A2.827 2.827 0 1 1 12 19.98a2.827 2.827 0 1 1-4.17-3.81A1 1 0 1 1 4 12"}],["path",{d:"m7.83 7.83 8.34 8.34"}]],E8=[["path",{d:"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"}]],W8=[["path",{d:"M17.28 9.05a5.5 5.5 0 1 0-10.56 0A5.5 5.5 0 1 0 12 17.66a5.5 5.5 0 1 0 5.28-8.6Z"}],["path",{d:"M12 17.66L12 22"}]],B1=[["path",{d:"m18 16 4-4-4-4"}],["path",{d:"m6 8-4 4 4 4"}],["path",{d:"m14.5 4-5 16"}]],I8=[["path",{d:"m16 18 6-6-6-6"}],["path",{d:"m8 6-6 6 6 6"}]],X8=[["path",{d:"M10 2v2"}],["path",{d:"M14 2v2"}],["path",{d:"M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"}],["path",{d:"M6 2v2"}]],N8=[["path",{d:"M11 10.27 7 3.34"}],["path",{d:"m11 13.73-4 6.93"}],["path",{d:"M12 22v-2"}],["path",{d:"M12 2v2"}],["path",{d:"M14 12h8"}],["path",{d:"m17 20.66-1-1.73"}],["path",{d:"m17 3.34-1 1.73"}],["path",{d:"M2 12h2"}],["path",{d:"m20.66 17-1.73-1"}],["path",{d:"m20.66 7-1.73 1"}],["path",{d:"m3.34 17 1.73-1"}],["path",{d:"m3.34 7 1.73 1"}],["circle",{cx:"12",cy:"12",r:"2"}],["circle",{cx:"12",cy:"12",r:"8"}]],K8=[["path",{d:"M13.744 17.736a6 6 0 1 1-7.48-7.48"}],["path",{d:"M15 6h1v4"}],["path",{d:"m6.134 14.768.866-.5 2 3.464"}],["circle",{cx:"16",cy:"8",r:"6"}]],z1=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 3v18"}]],e=[["path",{d:"M10.6 21H5a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v5.6"}],["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"M15 3v7.6"}],["path",{d:"m15.229 16.852-.924-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"m20.773 16.852.922-.383"}],["path",{d:"m20.773 19.148.922.383"}],["path",{d:"M9 3v18"}],["circle",{cx:"18",cy:"18",r:"3"}]],D1=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"M15 3v18"}]],J8=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7.5 3v18"}],["path",{d:"M12 3v18"}],["path",{d:"M16.5 3v18"}]],Q8=[["path",{d:"M14 3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1"}],["path",{d:"M19 3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1"}],["path",{d:"m7 15 3 3"}],["path",{d:"m7 21 3-3H5a2 2 0 0 1-2-2v-2"}],["rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"}],["rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}]],j8=[["path",{d:"M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"}]],Y8=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"}]],_8=[["path",{d:"M15.536 11.293a1 1 0 0 0 0 1.414l2.376 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z"}],["path",{d:"M2.297 11.293a1 1 0 0 0 0 1.414l2.377 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414L6.088 8.916a1 1 0 0 0-1.414 0z"}],["path",{d:"M8.916 17.912a1 1 0 0 0 0 1.415l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.415l-2.377-2.376a1 1 0 0 0-1.414 0z"}],["path",{d:"M8.916 4.674a1 1 0 0 0 0 1.414l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z"}]],x8=[["path",{d:"M12 18h6"}],["path",{d:"M6 18h.01"}],["path",{d:"M8 6h1"}],["rect",{x:"2",y:"14",width:"20",height:"8",rx:"2"}],["rect",{x:"4",y:"2",width:"16",height:"12",rx:"2"}]],ac=[["path",{d:"m20.9 18.55-8-15.98a1 1 0 0 0-1.8 0l-8 15.98"}],["ellipse",{cx:"12",cy:"19",rx:"9",ry:"3"}]],hc=[["path",{d:"M3 20a1 1 0 0 1-1-1v-1a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1Z"}],["path",{d:"M20 16a8 8 0 1 0-16 0"}],["path",{d:"M12 4v4"}],["path",{d:"M10 4h4"}]],tc=[["rect",{x:"2",y:"6",width:"20",height:"8",rx:"1"}],["path",{d:"M17 14v7"}],["path",{d:"M7 14v7"}],["path",{d:"M17 3v3"}],["path",{d:"M7 3v3"}],["path",{d:"M10 14 2.3 6.3"}],["path",{d:"m14 6 7.7 7.7"}],["path",{d:"m8 6 8 8"}]],F1=[["path",{d:"M16 2v2"}],["path",{d:"M17.915 21a6 6 0 10-12 0"}],["path",{d:"M8 2v2"}],["circle",{cx:"12",cy:"11",r:"4"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],dc=[["path",{d:"M16 2v2"}],["path",{d:"M7 21v-2a2 2 0 012-2h6a2 2 0 012 2v2"}],["path",{d:"M8 2v2"}],["circle",{cx:"12",cy:"10",r:"3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],cc=[["path",{d:"M22 7.7c0-.6-.4-1.2-.8-1.5l-6.3-3.9a1.72 1.72 0 0 0-1.7 0l-10.3 6c-.5.2-.9.8-.9 1.4v6.6c0 .5.4 1.2.8 1.5l6.3 3.9a1.72 1.72 0 0 0 1.7 0l10.3-6c.5-.3.9-1 .9-1.5Z"}],["path",{d:"M10 21.9V14L2.1 9.1"}],["path",{d:"m10 14 11.9-6.9"}],["path",{d:"M14 19.8v-8.1"}],["path",{d:"M18 17.5V9.4"}]],Mc=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 18a6 6 0 0 0 0-12v12z"}]],pc=[["path",{d:"M11 17h.01"}],["path",{d:"M11.496 2c.324-.016.558.292.529.615a4 4 0 004.235 4.368.713.713 0 01.758.757 4 4 0 004.366 4.237c.323-.03.63.204.614.527a10 10 0 01-2.915 6.566A1 1 0 114.93 4.918 10 10 0 0111.496 2"}],["path",{d:"M12 12h.01"}],["path",{d:"M16 16h.01"}],["path",{d:"M16 3h.01"}],["path",{d:"M21 4h.01"}],["path",{d:"M21 8h.01"}],["path",{d:"M7 14h.01"}],["path",{d:"M9 8h.01"}]],lc=[["path",{d:"M2 12h20"}],["path",{d:"M20 12v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8"}],["path",{d:"m4 8 16-4"}],["path",{d:"m8.86 6.78-.45-1.81a2 2 0 0 1 1.45-2.43l1.94-.48a2 2 0 0 1 2.43 1.46l.45 1.8"}]],ic=[["path",{d:"m12 15 2 2 4-4"}],["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]],nc=[["line",{x1:"12",x2:"18",y1:"15",y2:"15"}],["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]],ec=[["line",{x1:"15",x2:"15",y1:"12",y2:"18"}],["line",{x1:"12",x2:"18",y1:"15",y2:"15"}],["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]],rc=[["line",{x1:"12",x2:"18",y1:"18",y2:"12"}],["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]],oc=[["path",{d:"M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2"}],["rect",{x:"8",y:"8",width:"14",height:"14",rx:"2"}],["path",{d:"m12.5 12.5 5 5"}],["path",{d:"m12.5 17.5 5-5"}]],vc=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]],$c=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M9.17 14.83a4 4 0 1 0 0-5.66"}]],mc=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M14.83 14.83a4 4 0 1 1 0-5.66"}]],yc=[["path",{d:"M20 4v7a4 4 0 0 1-4 4H4"}],["path",{d:"m9 10-5 5 5 5"}]],sc=[["path",{d:"m15 10 5 5-5 5"}],["path",{d:"M4 4v7a4 4 0 0 0 4 4h12"}]],Cc=[["path",{d:"m14 15-5 5-5-5"}],["path",{d:"M20 4h-7a4 4 0 0 0-4 4v12"}]],uc=[["path",{d:"M14 9 9 4 4 9"}],["path",{d:"M20 20h-7a4 4 0 0 1-4-4V4"}]],gc=[["path",{d:"m10 15 5 5 5-5"}],["path",{d:"M4 4h7a4 4 0 0 1 4 4v12"}]],Ac=[["path",{d:"M20 20v-7a4 4 0 0 0-4-4H4"}],["path",{d:"M9 14 4 9l5-5"}]],Hc=[["path",{d:"m10 9 5-5 5 5"}],["path",{d:"M4 20h7a4 4 0 0 0 4-4V4"}]],Vc=[["path",{d:"m15 14 5-5-5-5"}],["path",{d:"M4 20v-7a4 4 0 0 1 4-4h12"}]],Sc=[["path",{d:"M12 20v2"}],["path",{d:"M12 2v2"}],["path",{d:"M16 9v6"}],["path",{d:"M17 20v2"}],["path",{d:"M17 2v2"}],["path",{d:"M2 12h2"}],["path",{d:"M2 17h2"}],["path",{d:"M2 7h2"}],["path",{d:"M20 12h2"}],["path",{d:"M20 17h2"}],["path",{d:"M20 7h2"}],["path",{d:"M7 20v2"}],["path",{d:"M7 2v2"}],["path",{d:"M8 13h4"}],["path",{d:"M8 15v-4a2 2 0 014 0v4"}],["rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}]],wc=[["path",{d:"M12 20v2"}],["path",{d:"M12 2v2"}],["path",{d:"M17 20v2"}],["path",{d:"M17 2v2"}],["path",{d:"M2 12h2"}],["path",{d:"M2 17h2"}],["path",{d:"M2 7h2"}],["path",{d:"M20 12h2"}],["path",{d:"M20 17h2"}],["path",{d:"M20 7h2"}],["path",{d:"M7 20v2"}],["path",{d:"M7 2v2"}],["rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}],["rect",{x:"8",y:"8",width:"8",height:"8",rx:"1"}]],Lc=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M10 9.3a2.8 2.8 0 0 0-3.5 1 3.1 3.1 0 0 0 0 3.4 2.7 2.7 0 0 0 3.5 1"}],["path",{d:"M17 9.3a2.8 2.8 0 0 0-3.5 1 3.1 3.1 0 0 0 0 3.4 2.7 2.7 0 0 0 3.5 1"}]],fc=[["path",{d:"M12.5 19H4a2 2 0 01-2-2V7a2 2 0 012-2h16a2 2 0 012 2v4"}],["path",{d:"M2 10h20"}],["path",{d:"M6 14h2"}],["path",{d:"m16 17 2 2 4-4"}]],kc=[["path",{d:"M22 13V7a2 2 0 00-2-2H4a2 2 0 00-2 2v10a2 2 0 002 2h8.536"}],["path",{d:"M22 10H2"}],["path",{d:"M6 14h2"}],["path",{d:"M16 17h6"}]],Pc=[["path",{d:"M22 11.354V7a2 2 0 00-2-2H4a2 2 0 00-2 2v10a2 2 0 002 2h8.536"}],["path",{d:"M22 10H2"}],["path",{d:"M6 14h2"}],["path",{d:"M16 17h6"}],["path",{d:"M19 14v6"}]],Bc=[["path",{d:"M15 16v1"}],["path",{d:"M16.963 7.734A1 1 0 0015.999 7H8.003a1 1 0 00-.964.734L4.073 18.467A2 2 0 006 21h12a2 2 0 001.927-2.532z"}],["path",{d:"M2.678 8.5A2 2 0 012 7V5a2 2 0 012-2h16a2 2 0 012 2v2a2 2 0 01-.676 1.499"}],["path",{d:"m9 21 2-14"}]],zc=[["path",{d:"M12.5 19H4a2 2 0 01-2-2V7a2 2 0 012-2h16a2 2 0 012 2v3.5"}],["path",{d:"M2 10h20"}],["path",{d:"M6 14h2"}],["path",{d:"m16.5 14.5 5 5"}],["path",{d:"m21.5 14.5-5 5"}]],Dc=[["rect",{width:"20",height:"14",x:"2",y:"5",rx:"2"}],["line",{x1:"2",x2:"22",y1:"10",y2:"10"}],["path",{d:"M6 14h2"}]],Fc=[["path",{d:"M10.2 18H4.774a1.5 1.5 0 0 1-1.352-.97 11 11 0 0 1 .132-6.487"}],["path",{d:"M18 10.2V4.774a1.5 1.5 0 0 0-.97-1.352 11 11 0 0 0-6.486.132"}],["path",{d:"M18 5a4 3 0 0 1 4 3 2 2 0 0 1-2 2 10 10 0 0 0-5.139 1.42"}],["path",{d:"M5 18a3 4 0 0 0 3 4 2 2 0 0 0 2-2 10 10 0 0 1 1.42-5.14"}],["path",{d:"M8.709 2.554a10 10 0 0 0-6.155 6.155 1.5 1.5 0 0 0 .676 1.626l9.807 5.42a2 2 0 0 0 2.718-2.718l-5.42-9.807a1.5 1.5 0 0 0-1.626-.676"}]],Rc=[["path",{d:"M6 2v14a2 2 0 0 0 2 2h14"}],["path",{d:"M18 22V8a2 2 0 0 0-2-2H2"}]],bc=[["path",{d:"M4 9a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h4a1 1 0 0 1 1 1v4a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-4a1 1 0 0 1 1-1h4a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-4a1 1 0 0 1-1-1V4a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4a1 1 0 0 1-1 1z"}]],Tc=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"22",x2:"18",y1:"12",y2:"12"}],["line",{x1:"6",x2:"2",y1:"12",y2:"12"}],["line",{x1:"12",x2:"12",y1:"6",y2:"2"}],["line",{x1:"12",x2:"12",y1:"22",y2:"18"}]],qc=[["path",{d:"M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"}],["path",{d:"M5 21h14"}]],Uc=[["path",{d:"M10 22v-8"}],["path",{d:"M2.336 8.89 10 14l11.715-7.029"}],["path",{d:"M22 14a2 2 0 0 1-.971 1.715l-10 6a2 2 0 0 1-2.138-.05l-6-4A2 2 0 0 1 2 16v-6a2 2 0 0 1 .971-1.715l10-6a2 2 0 0 1 2.138.05l6 4A2 2 0 0 1 22 8z"}]],Oc=[["path",{d:"M12 22v-9"}],["path",{d:"M14 4h1a3 3 0 013 3l-.004.125A4 4 0 0121 11v2"}],["path",{d:"m15.5 22 1.5-9"}],["path",{d:"M21 13a1 1 0 01.919 1.394l-2.74 6.394A2 2 0 0117.34 22H6.659a2 2 0 01-1.838-1.212l-2.74-6.394A1 1 0 013 13z"}],["path",{d:"M3 13v-2a4 4 0 013.003-3.875L6 7a3 3 0 013-3h1"}],["path",{d:"M8.5 22 7 13"}],["circle",{cx:"12",cy:"4",r:"2"}]],Gc=[["path",{d:"m6 8 1.75 12.28a2 2 0 0 0 2 1.72h4.54a2 2 0 0 0 2-1.72L18 8"}],["path",{d:"M5 8h14"}],["path",{d:"M7 15a6.47 6.47 0 0 1 5 0 6.47 6.47 0 0 0 5 0"}],["path",{d:"m12 8 1-6h2"}]],Zc=[["circle",{cx:"12",cy:"12",r:"8"}],["line",{x1:"3",x2:"6",y1:"3",y2:"6"}],["line",{x1:"21",x2:"18",y1:"3",y2:"6"}],["line",{x1:"3",x2:"6",y1:"21",y2:"18"}],["line",{x1:"21",x2:"18",y1:"21",y2:"18"}]],Ec=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5v14a9 3 0 0 0 18 0V5"}]],Wc=[["path",{d:"M11 11.31c1.17.56 1.54 1.69 3.5 1.69 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}],["path",{d:"M11.75 18c.35.5 1.45 1 2.75 1 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["path",{d:"M2 6h4"}],["path",{d:"M7 3a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1L10 4a1 1 0 0 0-1-1z"}]],Ic=[["path",{d:"m16 19 3 3 3-3"}],["path",{d:"M19 16v6"}],["path",{d:"M21 12.536V5"}],["path",{d:"M3 12A9 3 0 0 0 15.182 14.806"}],["path",{d:"M3 5V19A9 3 0 0 0 13.318 21.968"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],Xc=[["path",{d:"M19 22v-6"}],["path",{d:"M21 12.536V5"}],["path",{d:"m22 19-3-3-3 3"}],["path",{d:"M3 12A9 3 0 0 0 14.457 14.886"}],["path",{d:"M3 5V19A9 3 0 0 0 13.318 21.968"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],Nc=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 12a9 3 0 0 0 5 2.69"}],["path",{d:"M21 9.3V5"}],["path",{d:"M3 5v14a9 3 0 0 0 6.47 2.88"}],["path",{d:"M12 12v4h4"}],["path",{d:"M13 20a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L12 16"}]],Kc=[["path",{d:"m16 19 2 2 4-4"}],["path",{d:"M21 13.127V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}],["path",{d:"M3 5V19A9 3 0 0 0 13.318 21.968"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],Jc=[["path",{d:"M19 16v6"}],["path",{d:"M21 12.536V5"}],["path",{d:"M22 19h-6"}],["path",{d:"M3 12A9 3 0 0 0 15.1824 14.8061"}],["path",{d:"M3 5V19A9 3 0 0 0 13.318 21.968"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],Qc=[["path",{d:"M21 15V5"}],["path",{d:"M22 19h-6"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}],["path",{d:"M3 5V19A9 3 0 0 0 13.318 21.968"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],jc=[["path",{d:"M21 11.693V5"}],["path",{d:"m22 22-1.875-1.875"}],["path",{d:"M3 12a9 3 0 0 0 8.697 2.998"}],["path",{d:"M3 5v14a9 3 0 0 0 9.28 2.999"}],["circle",{cx:"18",cy:"18",r:"3"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],Yc=[["path",{d:"m17 17 5 5"}],["path",{d:"M19.323 13.744A9 3 0 0 0 21 12"}],["path",{d:"M21 13.127V5"}],["path",{d:"m22 17-5 5"}],["path",{d:"M3 12A9 3 0 0 0 13.563 14.954"}],["path",{d:"M3 5V19A9 3 0 0 0 13 21.981"}],["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}]],_c=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 15 21.84"}],["path",{d:"M21 5V8"}],["path",{d:"M21 12L18 17H22L19 22"}],["path",{d:"M3 12A9 3 0 0 0 14.59 14.87"}]],xc=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}]],a7=[["path",{d:"m13 21-3-3 3-3"}],["path",{d:"M20 18H10"}],["path",{d:"M3 11h.01"}],["rect",{x:"6",y:"3",width:"5",height:"8",rx:"2.5"}]],h7=[["path",{d:"M10 18h10"}],["path",{d:"m17 21 3-3-3-3"}],["path",{d:"M3 11h.01"}],["rect",{x:"15",y:"3",width:"5",height:"8",rx:"2.5"}],["rect",{x:"6",y:"3",width:"5",height:"8",rx:"2.5"}]],t7=[["path",{d:"M10.162 3.167A10 10 0 0 0 2 13a2 2 0 0 0 4 0v-1a2 2 0 0 1 4 0v4a2 2 0 0 0 4 0v-4a2 2 0 0 1 4 0v1a2 2 0 0 0 4-.006 10 10 0 0 0-8.161-9.826"}],["path",{d:"M20.804 14.869a9 9 0 0 1-17.608 0"}],["circle",{cx:"12",cy:"4",r:"2"}]],d7=[["path",{d:"M10 5a2 2 0 0 0-1.344.519l-6.328 5.74a1 1 0 0 0 0 1.481l6.328 5.741A2 2 0 0 0 10 19h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z"}],["path",{d:"m12 9 6 6"}],["path",{d:"m18 9-6 6"}]],c7=[["circle",{cx:"19",cy:"19",r:"2"}],["circle",{cx:"5",cy:"5",r:"2"}],["path",{d:"M6.48 3.66a10 10 0 0 1 13.86 13.86"}],["path",{d:"m6.41 6.41 11.18 11.18"}],["path",{d:"M3.66 6.48a10 10 0 0 0 13.86 13.86"}]],M7=[["path",{d:"M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0z"}],["path",{d:"M8 12h8"}]],R1=[["path",{d:"M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0Z"}],["path",{d:"M9.2 9.2h.01"}],["path",{d:"m14.5 9.5-5 5"}],["path",{d:"M14.7 14.8h.01"}]],p7=[["path",{d:"M12 8v8"}],["path",{d:"M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0z"}],["path",{d:"M8 12h8"}]],l7=[["path",{d:"M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41l-7.59-7.59a2.41 2.41 0 0 0-3.41 0Z"}]],i7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M12 12h.01"}]],n7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M15 9h.01"}],["path",{d:"M9 15h.01"}]],e7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M16 8h.01"}],["path",{d:"M12 12h.01"}],["path",{d:"M8 16h.01"}]],r7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M16 8h.01"}],["path",{d:"M8 8h.01"}],["path",{d:"M8 16h.01"}],["path",{d:"M16 16h.01"}]],o7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M16 8h.01"}],["path",{d:"M16 12h.01"}],["path",{d:"M16 16h.01"}],["path",{d:"M8 8h.01"}],["path",{d:"M8 12h.01"}],["path",{d:"M8 16h.01"}]],v7=[["rect",{width:"12",height:"12",x:"2",y:"10",rx:"2",ry:"2"}],["path",{d:"m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6"}],["path",{d:"M6 18h.01"}],["path",{d:"M10 14h.01"}],["path",{d:"M15 6h.01"}],["path",{d:"M18 9h.01"}]],$7=[["path",{d:"M12 3v14"}],["path",{d:"M5 10h14"}],["path",{d:"M5 21h14"}]],m7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M16 8h.01"}],["path",{d:"M8 8h.01"}],["path",{d:"M8 16h.01"}],["path",{d:"M16 16h.01"}],["path",{d:"M12 12h.01"}]],y7=[["circle",{cx:"12",cy:"12",r:"10"}],["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 12h.01"}]],s7=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M6 12c0-1.7.7-3.2 1.8-4.2"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M18 12c0 1.7-.7 3.2-1.8 4.2"}]],C7=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["circle",{cx:"12",cy:"12",r:"5"}],["path",{d:"M12 12h.01"}]],u7=[["circle",{cx:"12",cy:"12",r:"10"}],["circle",{cx:"12",cy:"12",r:"2"}]],g7=[["circle",{cx:"12",cy:"6",r:"1"}],["line",{x1:"5",x2:"19",y1:"12",y2:"12"}],["circle",{cx:"12",cy:"18",r:"1"}]],A7=[["path",{d:"M15 2c-1.35 1.5-2.092 3-2.5 4.5L14 8"}],["path",{d:"m17 6-2.891-2.891"}],["path",{d:"M2 15c3.333-3 6.667-3 10-3"}],["path",{d:"m2 2 20 20"}],["path",{d:"m20 9 .891.891"}],["path",{d:"M22 9c-1.5 1.35-3 2.092-4.5 2.5l-1-1"}],["path",{d:"M3.109 14.109 4 15"}],["path",{d:"m6.5 12.5 1 1"}],["path",{d:"m7 18 2.891 2.891"}],["path",{d:"M9 22c1.35-1.5 2.092-3 2.5-4.5L10 16"}]],H7=[["path",{d:"m10 16 1.5 1.5"}],["path",{d:"m14 8-1.5-1.5"}],["path",{d:"M15 2c-1.798 1.998-2.518 3.995-2.807 5.993"}],["path",{d:"m16.5 10.5 1 1"}],["path",{d:"m17 6-2.891-2.891"}],["path",{d:"M2 15c6.667-6 13.333 0 20-6"}],["path",{d:"m20 9 .891.891"}],["path",{d:"M3.109 14.109 4 15"}],["path",{d:"m6.5 12.5 1 1"}],["path",{d:"m7 18 2.891 2.891"}],["path",{d:"M9 22c1.798-1.998 2.518-3.995 2.807-5.993"}]],V7=[["path",{d:"M2 8h20"}],["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M6 16h12"}]],S7=[["path",{d:"M11.25 16.25h1.5L12 17z"}],["path",{d:"M16 14v.5"}],["path",{d:"M4.42 11.247A13.152 13.152 0 0 0 4 14.556C4 18.728 7.582 21 12 21s8-2.272 8-6.444a11.702 11.702 0 0 0-.493-3.309"}],["path",{d:"M8 14v.5"}],["path",{d:"M8.5 8.5c-.384 1.05-1.083 2.028-2.344 2.5-1.931.722-3.576-.297-3.656-1-.113-.994 1.177-6.53 4-7 1.923-.321 3.651.845 3.651 2.235A7.497 7.497 0 0 1 14 5.277c0-1.39 1.844-2.598 3.767-2.277 2.823.47 4.113 6.006 4 7-.08.703-1.725 1.722-3.656 1-1.261-.472-1.855-1.45-2.239-2.5"}]],w7=[["line",{x1:"12",x2:"12",y1:"2",y2:"22"}],["path",{d:"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"}]],L7=[["path",{d:"M10 21v-3a2 2 0 014 0v3"}],["path",{d:"M12 2v2"}],["path",{d:"M18 12v9"}],["path",{d:"M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2v-6a1 1 0 011-1h18a1 1 0 011 1z"}],["path",{d:"M4 12a8 8 0 0116 0"}],["path",{d:"M6 12v9"}]],f7=[["path",{d:"M20.5 10a2.5 2.5 0 0 1-2.4-3H18a2.95 2.95 0 0 1-2.6-4.4 10 10 0 1 0 6.3 7.1c-.3.2-.8.3-1.2.3"}],["circle",{cx:"12",cy:"12",r:"3"}]],k7=[["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.229 16.852-.924-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.773-.383.924"}],["path",{d:"M19 10.35V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.697-.382-.924"}],["path",{d:"M2 21h8.58"}],["path",{d:"m20.773 16.852.922-.383"}],["path",{d:"m20.773 19.148.922.383"}],["path",{d:"M9 12h.01"}],["circle",{cx:"18",cy:"18",r:"3"}]],P7=[["path",{d:"M19 8V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16"}],["path",{d:"M2 21h8"}],["path",{d:"M20 16v-2a2 2 0 00-4 0v2"}],["path",{d:"M9 12h.01"}],["rect",{x:"14",y:"16",width:"8",height:"5",rx:"1"}]],B7=[["path",{d:"M18 13v3"}],["path",{d:"M19 9V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16"}],["path",{d:"M2 21h8"}],["path",{d:"M9 12h.01"}],["rect",{x:"14",y:"13",width:"8",height:"8",rx:"1"}]],z7=[["path",{d:"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16"}],["path",{d:"M2 21h20"}],["path",{d:"M9 12h.01"}]],D7=[["path",{d:"M10 21H2"}],["path",{d:"M10 3H7a2 2 0 00-2 2v16"}],["path",{d:"M14 12h.01"}],["path",{d:"M19 21V5a2 2 0 00-1.675-1.974l-6.163-1.013A1 1 0 0010 3v18a1 1 0 001.124.992z"}],["path",{d:"M22 21h-3"}]],F7=[["path",{d:"M12 17v-3a1 1 0 011-1h6"}],["path",{d:"M19 17h-9a1 1 0 00-1 1v3"}],["path",{d:"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16"}],["path",{d:"M19 9h-3a1 1 0 00-1 1v3"}],["path",{d:"M22 21H2"}]],R7=[["circle",{cx:"12",cy:"12",r:"1"}]],b7=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]],T7=[["path",{d:"m12.99 6.74 1.93 3.44"}],["path",{d:"M19.136 12a10 10 0 0 1-14.271 0"}],["path",{d:"m21 21-2.16-3.84"}],["path",{d:"m3 21 8.02-14.26"}],["circle",{cx:"12",cy:"5",r:"2"}]],q7=[["path",{d:"M10 11h.01"}],["path",{d:"M14 6h.01"}],["path",{d:"M18 6h.01"}],["path",{d:"M6.5 13.1h.01"}],["path",{d:"M22 5c0 9-4 12-6 12s-6-3-6-12c0-2 2-3 6-3s6 1 6 3"}],["path",{d:"M17.4 9.9c-.8.8-2 .8-2.8 0"}],["path",{d:"M10.1 7.1C9 7.2 7.7 7.7 6 8.6c-3.5 2-4.7 3.9-3.7 5.6 4.5 7.8 9.5 8.4 11.2 7.4.9-.5 1.9-2.1 1.9-4.7"}],["path",{d:"M9.1 16.5c.3-1.1 1.4-1.7 2.4-1.4"}]],U7=[["path",{d:"M10 18a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H5a3 3 0 0 1-3-3 1 1 0 0 1 1-1z"}],["path",{d:"M13 10H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1l-.81 3.242a1 1 0 0 1-.97.758H8"}],["path",{d:"M14 4h3a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-3"}],["path",{d:"M18 6h4"}],["path",{d:"m5 10-2 8"}],["path",{d:"m7 18 2-8"}]],O7=[["path",{d:"M10 10 7 7"}],["path",{d:"m10 14-3 3"}],["path",{d:"m14 10 3-3"}],["path",{d:"m14 14 3 3"}],["path",{d:"M14.205 4.139a4 4 0 1 1 5.439 5.863"}],["path",{d:"M19.637 14a4 4 0 1 1-5.432 5.868"}],["path",{d:"M4.367 10a4 4 0 1 1 5.438-5.862"}],["path",{d:"M9.795 19.862a4 4 0 1 1-5.429-5.873"}],["rect",{x:"10",y:"8",width:"4",height:"8",rx:"1"}]],G7=[["path",{d:"M18.715 13.186C18.29 11.858 17.384 10.607 16 9.5c-2-1.6-3.5-4-4-6.5a10.7 10.7 0 0 1-.884 2.586"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.795 8.797A11 11 0 0 1 8 9.5C6 11.1 5 13 5 15a7 7 0 0 0 13.222 3.208"}]],Z7=[["path",{d:"M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z"}]],E7=[["path",{d:"M7 16.3c2.2 0 4-1.83 4-4.05 0-1.16-.57-2.26-1.71-3.19S7.29 6.75 7 5.3c-.29 1.45-1.14 2.84-2.29 3.76S3 11.1 3 12.25c0 2.22 1.8 4.05 4 4.05z"}],["path",{d:"M12.56 6.6A10.97 10.97 0 0 0 14 3.02c.5 2.5 2 4.9 4 6.5s3 3.5 3 5.5a6.98 6.98 0 0 1-11.91 4.97"}]],W7=[["path",{d:"m2 2 8 8"}],["path",{d:"m22 2-8 8"}],["ellipse",{cx:"12",cy:"9",rx:"10",ry:"5"}],["path",{d:"M7 13.4v7.9"}],["path",{d:"M12 14v8"}],["path",{d:"M17 13.4v7.9"}],["path",{d:"M2 9v8a10 5 0 0 0 20 0V9"}]],I7=[["path",{d:"M15.4 15.63a7.875 6 135 1 1 6.23-6.23 4.5 3.43 135 0 0-6.23 6.23"}],["path",{d:"m8.29 12.71-2.6 2.6a2.5 2.5 0 1 0-1.65 4.65A2.5 2.5 0 1 0 8.7 18.3l2.59-2.59"}]],X7=[["path",{d:"M17.596 12.768a2 2 0 1 0 2.829-2.829l-1.768-1.767a2 2 0 0 0 2.828-2.829l-2.828-2.828a2 2 0 0 0-2.829 2.828l-1.767-1.768a2 2 0 1 0-2.829 2.829z"}],["path",{d:"m2.5 21.5 1.4-1.4"}],["path",{d:"m20.1 3.9 1.4-1.4"}],["path",{d:"M5.343 21.485a2 2 0 1 0 2.829-2.828l1.767 1.768a2 2 0 1 0 2.829-2.829l-6.364-6.364a2 2 0 1 0-2.829 2.829l1.768 1.767a2 2 0 0 0-2.828 2.829z"}],["path",{d:"m9.6 14.4 4.8-4.8"}]],N7=[["path",{d:"M6 18.5a3.5 3.5 0 1 0 7 0c0-1.57.92-2.52 2.04-3.46"}],["path",{d:"M6 8.5c0-.75.13-1.47.36-2.14"}],["path",{d:"M8.8 3.15A6.5 6.5 0 0 1 19 8.5c0 1.63-.44 2.81-1.09 3.76"}],["path",{d:"M12.5 6A2.5 2.5 0 0 1 15 8.5M10 13a2 2 0 0 0 1.82-1.18"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],K7=[["path",{d:"M6 8.5a6.5 6.5 0 1 1 13 0c0 6-6 6-6 10a3.5 3.5 0 1 1-7 0"}],["path",{d:"M15 8.5a2.5 2.5 0 0 0-5 0v1a2 2 0 1 1 0 4"}]],J7=[["path",{d:"M7 3.34V5a3 3 0 0 0 3 3"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2 2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05"}],["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54"}],["path",{d:"M12 2a10 10 0 1 0 9.54 13"}],["path",{d:"M20 6V4a2 2 0 1 0-4 0v2"}],["rect",{width:"8",height:"5",x:"14",y:"6",rx:"1"}]],b1=[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05"}],["circle",{cx:"12",cy:"12",r:"10"}]],Q7=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a7 7 0 1 0 10 10"}]],j7=[["circle",{cx:"11.5",cy:"12.5",r:"3.5"}],["path",{d:"M3 8c0-3.5 2.5-6 6.5-6 5 0 4.83 3 7.5 5s5 2 5 6c0 4.5-2.5 6.5-7 6.5-2.5 0-2.5 2.5-6 2.5s-7-2-7-5.5c0-3 1.5-3 1.5-5C3.5 10 3 9 3 8Z"}]],Y7=[["path",{d:"m2 2 20 20"}],["path",{d:"M20 14.347V14c0-6-4-12-8-12-1.078 0-2.157.436-3.157 1.19"}],["path",{d:"M6.206 6.21C4.871 8.4 4 11.2 4 14a8 8 0 0 0 14.568 4.568"}]],_7=[["path",{d:"M12 2C8 2 4 8 4 14a8 8 0 0 0 16 0c0-6-4-12-8-12"}]],T1=[["path",{d:"m19 6-5.2-3.9a.5.5 0 0 0-.8.4V18"}],["circle",{cx:"9",cy:"18",r:"4"}]],q1=[["path",{d:"M21 16V3a1 1 0 0 0-1.28-.96l-10 2.917a1 1 0 0 0-.72.96V19"}],["circle",{cx:"18",cy:"16",r:"3"}],["circle",{cx:"6",cy:"19",r:"3"}]],x7=[["path",{d:"M4 13a1 1 0 0 1-.72-1.695l7.257-7.668a2 2 0 0 1 2.926 0l7.256 7.668A1 1 0 0 1 20 13z"}],["rect",{x:"3",y:"17",width:"18",height:"4",rx:"1"}]],a9=[["ellipse",{cx:"12",cy:"12",rx:"10",ry:"6"}]],U1=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"12",cy:"5",r:"1"}],["circle",{cx:"12",cy:"19",r:"1"}]],O1=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]],h9=[["path",{d:"M10 3h6"}],["path",{d:"M13 3v4"}],["path",{d:"M2 10v6"}],["path",{d:"M2 13h4"}],["path",{d:"M6 16a2 2 0 002 2h1a2 2 0 011.6.8l.3.4a2 2 0 001.6.8h2.264a2 2 0 001.789-1.106l1.67-3.341a1 1 0 01.895-.553H21a1 1 0 001-1v-4a1 1 0 00-1-1h-3.5a1 1 0 01-.8-.4l-.9-1.2A1 1 0 0015 7h-4a1 1 0 00-.8.4l-.9 1.2a1 1 0 01-.8.4H7a1 1 0 00-1 1z"}]],t9=[["path",{d:"M5 15a6.5 6.5 0 0 1 7 0 6.5 6.5 0 0 0 7 0"}],["path",{d:"M5 9a6.5 6.5 0 0 1 7 0 6.5 6.5 0 0 0 7 0"}],["line",{x1:"19",x2:"5",y1:"5",y2:"19"}]],d9=[["path",{d:"M5 15a6.5 6.5 0 0 1 7 0 6.5 6.5 0 0 0 7 0"}],["path",{d:"M5 9a6.5 6.5 0 0 1 7 0 6.5 6.5 0 0 0 7 0"}]],c9=[["line",{x1:"5",x2:"19",y1:"9",y2:"9"}],["line",{x1:"5",x2:"19",y1:"15",y2:"15"}],["line",{x1:"19",x2:"5",y1:"5",y2:"19"}]],M9=[["line",{x1:"5",x2:"19",y1:"9",y2:"9"}],["line",{x1:"5",x2:"19",y1:"15",y2:"15"}]],p9=[["path",{d:"M10 8v1"}],["path",{d:"M14 8v1"}],["path",{d:"M18 8v1"}],["path",{d:"M19 17a2 2 0 00-1.765 1.059l-.47.882A2 2 0 0115 20H9a2 2 0 01-1.765-1.059l-.47-.882A2 2 0 005 17H4a2 2 0 01-2-2V6a2 2 0 012-2h16a2 2 0 012 2v9a2 2 0 01-2 2z"}],["path",{d:"M6 8v1"}]],l9=[["path",{d:"M4 10h12"}],["path",{d:"M4 14h9"}],["path",{d:"M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2"}]],i9=[["path",{d:"M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21"}],["path",{d:"m5.082 11.09 8.828 8.828"}]],n9=[["path",{d:"M14 13h2a2 2 0 0 1 2 2v2a2 2 0 0 0 4 0v-6.998a2 2 0 0 0-.59-1.42L18 5"}],["path",{d:"M14 21V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v16"}],["path",{d:"M2 21h13"}],["path",{d:"M3 7h11"}],["path",{d:"m9 11-2 3h3l-2 3"}]],e9=[["path",{d:"m15 15 6 6"}],["path",{d:"m15 9 6-6"}],["path",{d:"M21 16v5h-5"}],["path",{d:"M21 8V3h-5"}],["path",{d:"M3 16v5h5"}],["path",{d:"m3 21 6-6"}],["path",{d:"M3 8V3h5"}],["path",{d:"M9 9 3 3"}]],r9=[["path",{d:"M15 3h6v6"}],["path",{d:"M10 14 21 3"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}]],o9=[["path",{d:"m15 18-.722-3.25"}],["path",{d:"M2 8a10.645 10.645 0 0 0 20 0"}],["path",{d:"m20 15-1.726-2.05"}],["path",{d:"m4 15 1.726-2.05"}],["path",{d:"m9 18 .722-3.25"}]],v9=[["path",{d:"M13.054 18.946a11 11 0 0 1-2.11 0"}],["path",{d:"M13.054 5.054a11 11 0 0 0-2.11-.001"}],["path",{d:"M17.072 6.274a11 11 0 0 1 1.753 1.173"}],["path",{d:"M18.825 16.552a11 11 0 0 1-1.753 1.174"}],["path",{d:"M2.514 13.303a11 11 0 0 1-.452-.954 1 1 0 0 1 0-.697 11 11 0 0 1 .45-.955"}],["path",{d:"M21.485 10.697a11 11 0 0 1 .453.955 1 1 0 0 1 0 .697 11 11 0 0 1-.453.954"}],["path",{d:"M5.173 7.448a11 11 0 0 1 1.753-1.174"}],["path",{d:"M6.926 17.726a11 11 0 0 1-1.753-1.174"}],["circle",{cx:"12",cy:"12",r:"3"}]],$9=[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"}],["path",{d:"m2 2 20 20"}]],m9=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]],G1=[["path",{d:"M14 10h2"}],["path",{d:"M8 10h2"}],["path",{d:"M8 16h8"}],["circle",{cx:"12",cy:"12",r:"10"}]],Z1=[["path",{d:"M15 12v-1.584"}],["path",{d:"M17 10a5 5 0 00-3 1"}],["path",{d:"M7 10a5 5 0 013 1"}],["path",{d:"M9 12v-1.584"}],["path",{d:"M9 17a5 5 0 016.001 0"}],["circle",{cx:"12",cy:"12",r:"10"}]],E1=[["path",{d:"M15 10V9"}],["path",{d:"M7.084 14.302a5.12 5.12 0 009.833 0 .24.24 0 00-.235-.302H7.32a.24.24 0 00-.235.302"}],["path",{d:"M9 10V9"}],["circle",{cx:"12",cy:"12",r:"10"}]],W1=[["path",{d:"M15 10V9"}],["path",{d:"M8 16h8"}],["path",{d:"M9 10V9"}],["circle",{cx:"12",cy:"12",r:"10"}]],I1=[["path",{d:"M15 10V9"}],["path",{d:"M9 10V9"}],["path",{d:"M9 16a5 5 0 016 0"}],["circle",{cx:"12",cy:"12",r:"10"}]],X1=[["path",{d:"M13.267 2.08a10 10 0 108.653 8.653"}],["path",{d:"M15 10V9"}],["path",{d:"M16 5h6"}],["path",{d:"M16.472 15a6 6 0 01-8.943 0"}],["path",{d:"M19 2v6"}],["path",{d:"M9 10V9"}]],N1=[["path",{d:"M15 10V9"}],["path",{d:"M16.472 15a6 6 0 01-8.943 0"}],["path",{d:"M9 10V9"}],["circle",{cx:"12",cy:"12",r:"10"}]],y9=[["path",{d:"M12 16h.01"}],["path",{d:"M16 16h.01"}],["path",{d:"M3 19a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8.5a.5.5 0 0 0-.769-.422l-4.462 2.844A.5.5 0 0 1 15 10.5v-2a.5.5 0 0 0-.769-.422L9.77 10.922A.5.5 0 0 1 9 10.5V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2z"}],["path",{d:"M8 16h.01"}]],s9=[["path",{d:"M10.827 16.379a6.082 6.082 0 0 1-8.618-7.002l5.412 1.45a6.082 6.082 0 0 1 7.002-8.618l-1.45 5.412a6.082 6.082 0 0 1 8.618 7.002l-5.412-1.45a6.082 6.082 0 0 1-7.002 8.618l1.45-5.412Z"}],["path",{d:"M12 12v.01"}]],C9=[["path",{d:"M12 6a2 2 0 0 1 3.414-1.414l6 6a2 2 0 0 1 0 2.828l-6 6A2 2 0 0 1 12 18z"}],["path",{d:"M2 6a2 2 0 0 1 3.414-1.414l6 6a2 2 0 0 1 0 2.828l-6 6A2 2 0 0 1 2 18z"}]],u9=[["path",{d:"M10.083 5.428 5.57 4.083a2 2 0 10.001 3.834l4.512-1.345"}],["path",{d:"M12 8v3"}],["path",{d:"m13.917 5.428 4.511-1.345a2 2 0 110 3.834l-4.51-1.345"}],["path",{d:"M18 17v-4.006"}],["path",{d:"M22 11v8"}],["path",{d:"M22 12h-3a1 1 0 00-1 .994h-2.539a4 4 0 00-6.915-.012L7 13a5 5 0 00-5 5v1a1 1 0 001 1h2a1 1 0 001-1v-1a1 1 0 01.995-1l1.552.018a4 4 0 006.907 0L18 17a1 1 0 001 1h3"}],["circle",{cx:"12",cy:"6",r:"2"}]],g9=[["path",{d:"M4 3 2 5v15c0 .6.4 1 1 1h2c.6 0 1-.4 1-1V5Z"}],["path",{d:"M6 8h4"}],["path",{d:"M6 18h4"}],["path",{d:"m12 3-2 2v15c0 .6.4 1 1 1h2c.6 0 1-.4 1-1V5Z"}],["path",{d:"M14 8h4"}],["path",{d:"M14 18h4"}],["path",{d:"m20 3-2 2v15c0 .6.4 1 1 1h2c.6 0 1-.4 1-1V5Z"}]],A9=[["path",{d:"M14.086 18.412A2 2 0 0112.67 19H5v-7.672a2 2 0 01.586-1.414L11.75 3.75a6 6 0 118.49 8.49z"}],["path",{d:"M16 8 2 22"}],["path",{d:"M17.488 15H9"}]],H9=[["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M12 2v4"}],["path",{d:"m6.8 15-3.5 2"}],["path",{d:"m20.7 7-3.5 2"}],["path",{d:"M6.8 9 3.3 7"}],["path",{d:"m20.7 17-3.5-2"}],["path",{d:"m9 22 3-8 3 8"}],["path",{d:"M8 22h8"}],["path",{d:"M18 18.7a9 9 0 1 0-12 0"}]],K1=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m8 18 4-4"}],["path",{d:"M8 10v8h8"}]],V9=[["path",{d:"M13.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v11.5"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 12v-1"}],["path",{d:"M8 18v-2"}],["path",{d:"M8 7V6"}],["circle",{cx:"8",cy:"20",r:"2"}]],J1=[["path",{d:"M13 22h5a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v3.3"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m7.69 16.479 1.29 4.88a.5.5 0 0 1-.698.591l-1.843-.849a1 1 0 0 0-.879.001l-1.846.85a.5.5 0 0 1-.692-.593l1.29-4.88"}],["circle",{cx:"6",cy:"14",r:"3"}]],Q1=[["path",{d:"M14 22h4a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M5 14a1 1 0 0 0-1 1v2a1 1 0 0 1-1 1 1 1 0 0 1 1 1v2a1 1 0 0 0 1 1"}],["path",{d:"M9 22a1 1 0 0 0 1-1v-2a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-2a1 1 0 0 0-1-1"}]],S9=[["path",{d:"M14 2v5a1 1 0 001 1h5"}],["path",{d:"M14.692 22H18a2 2 0 002-2V8a2.4 2.4 0 00-.706-1.706l-3.588-3.588A2.4 2.4 0 0014 2H6a2 2 0 00-2 2v3.804"}],["path",{d:"M2.264 13.752 7 16.5l4.737-2.748"}],["path",{d:"M2.995 13.014A2 2 0 002 14.744v3.516a2 2 0 00.996 1.73l3 1.74a2 2 0 002.008 0l3-1.74A2 2 0 0012 18.26v-3.517a2 2 0 00-.995-1.73l-3-1.742a2 2 0 00-1.892-.064z"}],["path",{d:"M7 16.5V22"}]],j1=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1"}],["path",{d:"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1"}]],Y1=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 18v-2"}],["path",{d:"M12 18v-4"}],["path",{d:"M16 18v-6"}]],_1=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 18v-1"}],["path",{d:"M12 18v-6"}],["path",{d:"M16 18v-3"}]],x1=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m16 13-3.5 3.5-2-2L8 17"}]],a2=[["path",{d:"M10.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m14 20 2 2 4-4"}]],h2=[["path",{d:"M15.941 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.704l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v3.512"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M4.017 11.512a6 6 0 1 0 8.466 8.475"}],["path",{d:"M9 16a1 1 0 0 1-1-1v-4c0-.552.45-1.008.995-.917a6 6 0 0 1 4.922 4.922c.091.544-.365.995-.917.995z"}]],w9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m9 15 2 2 4-4"}]],L9=[["path",{d:"M16 22h2a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v2.85"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 14v2.2l1.6 1"}],["circle",{cx:"8",cy:"16",r:"6"}]],t2=[["path",{d:"M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m5 16-3 3 3 3"}],["path",{d:"m9 22 3-3-3-3"}]],f9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 12.5 8 15l2 2.5"}],["path",{d:"m14 12.5 2 2.5-2 2.5"}]],d2=[["path",{d:"M15 8a1 1 0 0 1-1-1V2a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8z"}],["path",{d:"M20 8v12a2 2 0 0 1-2 2h-4.182"}],["path",{d:"m3.305 19.53.923-.382"}],["path",{d:"M4 10.592V4a2 2 0 0 1 2-2h8"}],["path",{d:"m4.228 16.852-.924-.383"}],["path",{d:"m5.852 15.228-.383-.923"}],["path",{d:"m5.852 20.772-.383.924"}],["path",{d:"m8.148 15.228.383-.923"}],["path",{d:"m8.53 21.696-.382-.924"}],["path",{d:"m9.773 16.852.922-.383"}],["path",{d:"m9.773 19.148.922.383"}],["circle",{cx:"7",cy:"18",r:"3"}]],k9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M9 10h6"}],["path",{d:"M12 13V7"}],["path",{d:"M9 17h6"}]],P9=[["path",{d:"M4 12V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 16h2v6"}],["path",{d:"M10 22h4"}],["rect",{x:"2",y:"16",width:"4",height:"6",rx:"2"}]],B9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M12 18v-6"}],["path",{d:"m9 15 3 3 3-3"}]],c2=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M12 9v4"}],["path",{d:"M12 17h.01"}]],r=[["path",{d:"M4 6.835V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-.343"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M2 19a2 2 0 0 1 4 0v1a2 2 0 0 1-4 0v-4a6 6 0 0 1 12 0v4a2 2 0 0 1-4 0v-1a2 2 0 0 1 4 0"}]],z9=[["path",{d:"M13 22h5a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v7"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M3.62 18.8A2.25 2.25 0 1 1 7 15.836a2.25 2.25 0 1 1 3.38 2.966l-2.626 2.856a1 1 0 0 1-1.507 0z"}]],D9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["circle",{cx:"10",cy:"12",r:"2"}],["path",{d:"m20 17-1.296-1.296a2.41 2.41 0 0 0-3.408 0L9 22"}]],F9=[["path",{d:"M4 11V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M2 15h10"}],["path",{d:"m9 18 3-3-3-3"}]],M2=[["path",{d:"M4 9.8V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M9 17v-2a2 2 0 0 0-4 0v2"}],["rect",{width:"8",height:"5",x:"3",y:"17",rx:"1"}]],p2=[["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M4 12v6"}],["path",{d:"M4 14h2"}],["path",{d:"M9.65 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v4"}],["circle",{cx:"4",cy:"20",r:"2"}]],l2=[["path",{d:"M20 14V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M14 18h6"}]],R9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M9 15h6"}]],b9=[["path",{d:"M11.65 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v10.35"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 20v-7l3 1.474"}],["circle",{cx:"6",cy:"20",r:"2"}]],T9=[["path",{d:"M4.226 20.925A2 2 0 0 0 6 22h12a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v3.127"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m5 11-3 3"}],["path",{d:"m5 17-3-3h10"}]],i2=[["path",{d:"M14.364 13.634a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506l4.013-4.009a1 1 0 0 0-3.004-3.004z"}],["path",{d:"M14.487 7.858A1 1 0 0 1 14 7V2"}],["path",{d:"M20 19.645V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l2.516 2.516"}],["path",{d:"M8 18h1"}]],n2=[["path",{d:"M12.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v9.34"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10.378 12.622a1 1 0 0 1 3 3.003L8.36 20.637a2 2 0 0 1-.854.506l-2.867.837a.5.5 0 0 1-.62-.62l.836-2.869a2 2 0 0 1 .506-.853z"}]],e2=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M15.033 13.44a.647.647 0 0 1 0 1.12l-4.065 2.352a.645.645 0 0 1-.968-.56v-4.704a.645.645 0 0 1 .967-.56z"}]],r2=[["path",{d:"M11.35 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v5.35"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M14 19h6"}],["path",{d:"M17 16v6"}]],q9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M9 15h6"}],["path",{d:"M12 18v-6"}]],o2=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M12 17h.01"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}]],U9=[["path",{d:"M20 10V8a2.4 2.4 0 0 0-.706-1.704l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.35"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M16 14a2 2 0 0 0-2 2"}],["path",{d:"M16 22a2 2 0 0 1-2-2"}],["path",{d:"M20 14a2 2 0 0 1 2 2"}],["path",{d:"M20 22a2 2 0 0 0 2-2"}]],v2=[["path",{d:"M11.1 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.589 3.588A2.4 2.4 0 0 1 20 8v3.25"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m21 22-2.88-2.88"}],["circle",{cx:"16",cy:"17",r:"3"}]],O9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["circle",{cx:"11.5",cy:"14.5",r:"2.5"}],["path",{d:"M13.3 16.3 15 18"}]],$2=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 15h.01"}],["path",{d:"M11.5 13.5a2.5 2.5 0 0 1 0 3"}],["path",{d:"M15 12a5 5 0 0 1 0 6"}]],G9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 12h8"}],["path",{d:"M10 11v2"}],["path",{d:"M8 17h8"}],["path",{d:"M14 16v2"}]],Z9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 13h2"}],["path",{d:"M14 13h2"}],["path",{d:"M8 17h2"}],["path",{d:"M14 17h2"}]],E9=[["path",{d:"M11 21a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1"}],["path",{d:"M16 16a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1"}],["path",{d:"M21 6a2 2 0 0 0-.586-1.414l-2-2A2 2 0 0 0 17 2h-3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1z"}]],W9=[["path",{d:"M4 11V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h7"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m10 18 3-3-3-3"}]],I9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m8 16 2-2-2-2"}],["path",{d:"M12 18h4"}]],X9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 9H8"}],["path",{d:"M16 13H8"}],["path",{d:"M16 17H8"}]],m2=[["path",{d:"M12 22h6a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M3 16v-1.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5V16"}],["path",{d:"M6 22h2"}],["path",{d:"M7 14v8"}]],N9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M11 18h2"}],["path",{d:"M12 12v6"}],["path",{d:"M9 13v-.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v.5"}]],K9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M12 12v6"}],["path",{d:"m15 15-3-3-3 3"}]],J9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M16 22a4 4 0 0 0-8 0"}],["circle",{cx:"12",cy:"15",r:"3"}]],y2=[["path",{d:"M4 12V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m10 17.843 3.033-1.755a.64.64 0 0 1 .967.56v4.704a.65.65 0 0 1-.967.56L10 20.157"}],["rect",{width:"7",height:"6",x:"3",y:"16",rx:"1"}]],Q9=[["path",{d:"M4 11.55V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-1.95"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M12 15a5 5 0 0 1 0 6"}],["path",{d:"M8 14.502a.5.5 0 0 0-.826-.381l-1.893 1.631a1 1 0 0 1-.651.243H3.5a.5.5 0 0 0-.5.501v3.006a.5.5 0 0 0 .5.501h1.129a1 1 0 0 1 .652.243l1.893 1.633a.5.5 0 0 0 .826-.38z"}]],s2=[["path",{d:"M11 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v5"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m15 17 5 5"}],["path",{d:"m20 17-5 5"}]],j9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"m14.5 12.5-5 5"}],["path",{d:"m9.5 12.5 5 5"}]],Y9=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}]],_9=[["path",{d:"M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8"}],["path",{d:"M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z"}],["path",{d:"M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1"}]],x9=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 3v18"}],["path",{d:"M3 7.5h4"}],["path",{d:"M3 12h18"}],["path",{d:"M3 16.5h4"}],["path",{d:"M17 3v18"}],["path",{d:"M17 7.5h4"}],["path",{d:"M17 16.5h4"}]],C2=[["path",{d:"M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4"}],["path",{d:"M14 13.12c0 2.38 0 6.38-1 8.88"}],["path",{d:"M17.29 21.02c.12-.6.43-2.3.5-3.02"}],["path",{d:"M2 12a10 10 0 0 1 18-6"}],["path",{d:"M2 16h.01"}],["path",{d:"M21.8 16c.2-2 .131-5.354 0-6"}],["path",{d:"M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2"}],["path",{d:"M8.65 22c.21-.66.45-1.32.57-2"}],["path",{d:"M9 6.8a6 6 0 0 1 9 5.2v2"}]],aM=[["path",{d:"M15 6.5V3a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3.5"}],["path",{d:"M9 18h8"}],["path",{d:"M18 3h-3"}],["path",{d:"M11 3a6 6 0 0 0-6 6v11"}],["path",{d:"M5 13h4"}],["path",{d:"M17 10a4 4 0 0 0-8 0v10a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2Z"}]],hM=[["path",{d:"M18 12.47v.03m0-.5v.47m-.475 5.056A6.744 6.744 0 0 1 15 18c-3.56 0-7.56-2.53-8.5-6 .348-1.28 1.114-2.433 2.121-3.38m3.444-2.088A8.802 8.802 0 0 1 15 6c3.56 0 6.06 2.54 7 6-.309 1.14-.786 2.177-1.413 3.058"}],["path",{d:"M7 10.67C7 8 5.58 5.97 2.73 5.5c-1 1.5-1 5 .23 6.5-1.24 1.5-1.24 5-.23 6.5C5.58 18.03 7 16 7 13.33m7.48-4.372A9.77 9.77 0 0 1 16 6.07m0 11.86a9.77 9.77 0 0 1-1.728-3.618"}],["path",{d:"m16.01 17.93-.23 1.4A2 2 0 0 1 13.8 21H9.5a5.96 5.96 0 0 0 1.49-3.98M8.53 3h5.27a2 2 0 0 1 1.98 1.67l.23 1.4M2 2l20 20"}]],tM=[["path",{d:"M2 16s9-15 20-4C11 23 2 8 2 8"}]],dM=[["path",{d:"M6.5 12c.94-3.46 4.94-6 8.5-6 3.56 0 6.06 2.54 7 6-.94 3.47-3.44 6-7 6s-7.56-2.53-8.5-6Z"}],["path",{d:"M18 12v.5"}],["path",{d:"M16 17.93a9.77 9.77 0 0 1 0-11.86"}],["path",{d:"M7 10.67C7 8 5.58 5.97 2.73 5.5c-1 1.5-1 5 .23 6.5-1.24 1.5-1.24 5-.23 6.5C5.58 18.03 7 16 7 13.33"}],["path",{d:"M10.46 7.26C10.2 5.88 9.17 4.24 8 3h5.8a2 2 0 0 1 1.98 1.67l.23 1.4"}],["path",{d:"m16.01 17.93-.23 1.4A2 2 0 0 1 13.8 21H9.5a5.96 5.96 0 0 0 1.49-3.98"}]],cM=[["path",{d:"m17.586 11.414-5.93 5.93a1 1 0 0 1-8-8l3.137-3.137a.707.707 0 0 1 1.207.5V10"}],["path",{d:"M20.414 8.586 22 7"}],["circle",{cx:"19",cy:"10",r:"2"}]],MM=[["path",{d:"M4 11h1"}],["path",{d:"M8 15a2 2 0 0 1-4 0V3a1 1 0 0 1 1-1h.5C14 2 20 9 20 18v4"}],["circle",{cx:"18",cy:"18",r:"2"}]],pM=[["path",{d:"M16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528"}],["path",{d:"m2 2 20 20"}],["path",{d:"M4 22V4"}],["path",{d:"M7.656 2H8c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10.347"}]],lM=[["path",{d:"M18 22V2.8a.8.8 0 0 0-1.17-.71L5.45 7.78a.8.8 0 0 0 0 1.44L18 15.5"}]],iM=[["path",{d:"M6 22V2.8a.8.8 0 0 1 1.17-.71l11.38 5.69a.8.8 0 0 1 0 1.44L6 15.5"}]],nM=[["path",{d:"M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528"}]],eM=[["path",{d:"M12 2c1 3 2.5 3.5 3.5 4.5A5 5 0 0 1 17 10a5 5 0 1 1-10 0c0-.3 0-.6.1-.9a2 2 0 1 0 3.3-2C8 4.5 11 2 12 2Z"}],["path",{d:"m5 22 14-4"}],["path",{d:"m5 18 14 4"}]],rM=[["path",{d:"M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4"}]],oM=[["path",{d:"M11.652 6H18"}],["path",{d:"M12 13v1"}],["path",{d:"M16 16v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-8a4 4 0 0 0-.8-2.4l-.6-.8A3 3 0 0 1 6 7V6"}],["path",{d:"m2 2 20 20"}],["path",{d:"M7.649 2H17a1 1 0 0 1 1 1v4a3 3 0 0 1-.6 1.8l-.6.8a4 4 0 0 0-.55 1.007"}]],vM=[["path",{d:"M12 13v1"}],["path",{d:"M17 2a1 1 0 0 1 1 1v4a3 3 0 0 1-.6 1.8l-.6.8A4 4 0 0 0 16 12v8a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-8a4 4 0 0 0-.8-2.4l-.6-.8A3 3 0 0 1 6 7V3a1 1 0 0 1 1-1z"}],["path",{d:"M6 6h12"}]],$M=[["path",{d:"M10 2v2.343"}],["path",{d:"M14 2v6.343"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20 20a2 2 0 0 1-2 2H6a2 2 0 0 1-1.755-2.96l5.227-9.563"}],["path",{d:"M6.453 15H15"}],["path",{d:"M8.5 2h7"}]],mM=[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"}],["path",{d:"M6.453 15h11.094"}],["path",{d:"M8.5 2h7"}]],yM=[["path",{d:"M10 2v6.292a7 7 0 1 0 4 0V2"}],["path",{d:"M5 15h14"}],["path",{d:"M8.5 2h7"}]],sM=[["path",{d:"M12 5a3 3 0 1 1 3 3m-3-3a3 3 0 1 0-3 3m3-3v1M9 8a3 3 0 1 0 3 3M9 8h1m5 0a3 3 0 1 1-3 3m3-3h-1m-2 3v-1"}],["circle",{cx:"12",cy:"8",r:"2"}],["path",{d:"M12 10v12"}],["path",{d:"M12 22c4.2 0 7-1.667 7-5-4.2 0-7 1.667-7 5Z"}],["path",{d:"M12 22c-4.2 0-7-1.667-7-5 4.2 0 7 1.667 7 5Z"}]],CM=[["circle",{cx:"12",cy:"12",r:"3"}],["path",{d:"M12 16.5A4.5 4.5 0 1 1 7.5 12 4.5 4.5 0 1 1 12 7.5a4.5 4.5 0 1 1 4.5 4.5 4.5 4.5 0 1 1-4.5 4.5"}],["path",{d:"M12 7.5V9"}],["path",{d:"M7.5 12H9"}],["path",{d:"M16.5 12H15"}],["path",{d:"M12 16.5V15"}],["path",{d:"m8 8 1.88 1.88"}],["path",{d:"M14.12 9.88 16 8"}],["path",{d:"m8 16 1.88-1.88"}],["path",{d:"M14.12 14.12 16 16"}]],uM=[["circle",{cx:"12",cy:"12",r:"3"}],["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}]],gM=[["path",{d:"M2 12h6"}],["path",{d:"M22 12h-6"}],["path",{d:"M12 2v2"}],["path",{d:"M12 8v2"}],["path",{d:"M12 14v2"}],["path",{d:"M12 20v2"}],["path",{d:"m19 9-3 3 3 3"}],["path",{d:"m5 15 3-3-3-3"}]],AM=[["path",{d:"M12 22v-6"}],["path",{d:"M12 8V2"}],["path",{d:"M4 12H2"}],["path",{d:"M10 12H8"}],["path",{d:"M16 12h-2"}],["path",{d:"M22 12h-2"}],["path",{d:"m15 19-3-3-3 3"}],["path",{d:"m15 5-3 3-3-3"}]],HM=[["circle",{cx:"15",cy:"19",r:"2"}],["path",{d:"M20.9 19.8A2 2 0 0 0 22 18V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h5.1"}],["path",{d:"M15 11v-1"}],["path",{d:"M15 17v-2"}]],VM=[["path",{d:"M12 6v7.751a.25.25 0 00.407.195l2.28-1.834a.5.5 0 01.627 0l2.28 1.834a.25.25 0 00.406-.195V6"}],["path",{d:"M20 20a2 2 0 002-2V8a2 2 0 00-2-2h-7.9a2 2 0 01-1.69-.9L9.6 3.9A2 2 0 007.93 3H4a2 2 0 00-2 2v13a2 2 0 002 2z"}]],SM=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"m9 13 2 2 4-4"}]],wM=[["path",{d:"M16 14v2.2l1.6 1"}],["path",{d:"M7 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2"}],["circle",{cx:"16",cy:"16",r:"6"}]],LM=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"M2 10h20"}]],fM=[["path",{d:"M10 10.5 8 13l2 2.5"}],["path",{d:"m14 10.5 2 2.5-2 2.5"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2z"}]],u2=[["path",{d:"M10.3 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.98a2 2 0 0 1 1.69.9l.66 1.2A2 2 0 0 0 12 6h8a2 2 0 0 1 2 2v3.3"}],["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.228 16.852-.923-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"m20.772 16.852.924-.383"}],["path",{d:"m20.772 19.148.924.383"}],["circle",{cx:"18",cy:"18",r:"3"}]],kM=[["path",{d:"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"}],["circle",{cx:"12",cy:"13",r:"1"}]],PM=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"M12 10v6"}],["path",{d:"m15 13-3 3-3-3"}]],BM=[["path",{d:"M18 19a5 5 0 0 1-5-5v8"}],["path",{d:"M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5"}],["circle",{cx:"13",cy:"12",r:"2"}],["circle",{cx:"20",cy:"19",r:"2"}]],zM=[["circle",{cx:"12",cy:"13",r:"2"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"M14 13h3"}],["path",{d:"M7 13h3"}]],DM=[["path",{d:"M10.638 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v3.417"}],["path",{d:"M14.62 18.8A2.25 2.25 0 1 1 18 15.836a2.25 2.25 0 1 1 3.38 2.966l-2.626 2.856a.998.998 0 0 1-1.507 0z"}]],FM=[["path",{d:"M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1"}],["path",{d:"M2 13h10"}],["path",{d:"m9 16 3-3-3-3"}]],RM=[["path",{d:"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"}],["path",{d:"M8 10v4"}],["path",{d:"M12 10v2"}],["path",{d:"M16 10v6"}]],bM=[["path",{d:"M13 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v1.36"}],["path",{d:"M19 12v6"}],["path",{d:"M19 14h2"}],["circle",{cx:"19",cy:"20",r:"2"}]],TM=[["rect",{width:"8",height:"5",x:"14",y:"17",rx:"1"}],["path",{d:"M10 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v2.5"}],["path",{d:"M20 17v-2a2 2 0 1 0-4 0v2"}]],qM=[["path",{d:"M9 13h6"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}]],UM=[["path",{d:"m6 14 1.45-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.55 6a2 2 0 0 1-1.94 1.5H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.93a2 2 0 0 1 1.66.9l.82 1.2a2 2 0 0 0 1.66.9H18a2 2 0 0 1 2 2v2"}],["circle",{cx:"14",cy:"15",r:"1"}]],OM=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"}]],GM=[["path",{d:"M2 7.5V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-1.5"}],["path",{d:"M2 13h10"}],["path",{d:"m5 10-3 3 3 3"}]],g2=[["path",{d:"M2 11.5V5a2 2 0 0 1 2-2h3.9c.7 0 1.3.3 1.7.9l.8 1.2c.4.6 1 .9 1.7.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-9.5"}],["path",{d:"M11.378 13.626a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}]],ZM=[["path",{d:"M12 10v6"}],["path",{d:"M9 13h6"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}]],EM=[["path",{d:"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"}],["circle",{cx:"12",cy:"13",r:"2"}],["path",{d:"M12 15v5"}]],WM=[["circle",{cx:"11.5",cy:"12.5",r:"2.5"}],["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"M13.3 14.3 15 16"}]],IM=[["path",{d:"M10.7 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v4.1"}],["path",{d:"m21 21-1.9-1.9"}],["circle",{cx:"17",cy:"17",r:"3"}]],XM=[["path",{d:"M2 9.35V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h7"}],["path",{d:"m8 16 3-3-3-3"}]],NM=[["path",{d:"M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v.5"}],["path",{d:"M12 10v4h4"}],["path",{d:"m12 14 1.535-1.605a5 5 0 0 1 8 1.5"}],["path",{d:"M22 22v-4h-4"}],["path",{d:"m22 18-1.535 1.605a5 5 0 0 1-8-1.5"}]],KM=[["path",{d:"M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 15 3h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z"}],["path",{d:"M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H13a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z"}],["path",{d:"M3 5a2 2 0 0 0 2 2h3"}],["path",{d:"M3 3v13a2 2 0 0 0 2 2h3"}]],JM=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"M12 10v6"}],["path",{d:"m9 13 3-3 3 3"}]],QM=[["path",{d:"M10.254 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2V9.255"}],["path",{d:"M22 22a4 4 0 0 0-8 0"}],["circle",{cx:"18",cy:"15",r:"3"}]],jM=[["path",{d:"M5.254 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2V9.255"}],["circle",{cx:"13",cy:"15",r:"3"}],["path",{d:"M17 22a4 4 0 0 0-8 0"}],["path",{d:"M22 22A4 4 0 0 0 20.24 18.686 1 1 0 0 1 20.1 17.143 3 3 0 0 0 19.5 12.402"}]],YM=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}],["path",{d:"m9.5 10.5 5 5"}],["path",{d:"m14.5 10.5-5 5"}]],_M=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"}]],xM=[["path",{d:"M20 5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h2.5a1.5 1.5 0 0 1 1.2.6l.6.8a1.5 1.5 0 0 0 1.2.6z"}],["path",{d:"M3 8.268a2 2 0 0 0-1 1.738V19a2 2 0 0 0 2 2h11a2 2 0 0 0 1.732-1"}]],ap=[["path",{d:"M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z"}],["path",{d:"M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z"}],["path",{d:"M16 17h4"}],["path",{d:"M4 13h4"}]],hp=[["path",{d:"M12 12H5a2 2 0 0 0-2 2v5"}],["path",{d:"M15 19h7"}],["path",{d:"M16 19V2"}],["path",{d:"M6 12V7a2 2 0 0 1 2-2h2.172a2 2 0 0 1 1.414.586l3.828 3.828A2 2 0 0 1 16 10.828"}],["path",{d:"M7 19h4"}],["circle",{cx:"13",cy:"19",r:"2"}],["circle",{cx:"5",cy:"19",r:"2"}]],tp=[["path",{d:"M4 14h6"}],["path",{d:"M4 2h10"}],["rect",{x:"4",y:"18",width:"16",height:"4",rx:"1"}],["rect",{x:"4",y:"6",width:"16",height:"4",rx:"1"}]],dp=[["path",{d:"m15 17 5-5-5-5"}],["path",{d:"M4 18v-2a4 4 0 0 1 4-4h12"}]],cp=[["line",{x1:"22",x2:"2",y1:"6",y2:"6"}],["line",{x1:"22",x2:"2",y1:"18",y2:"18"}],["line",{x1:"6",x2:"6",y1:"2",y2:"22"}],["line",{x1:"18",x2:"18",y1:"2",y2:"22"}]],Mp=[["path",{d:"M14 13h2a2 2 0 0 1 2 2v2a2 2 0 0 0 4 0v-6.998a2 2 0 0 0-.59-1.42L18 5"}],["path",{d:"M14 21V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v16"}],["path",{d:"M2 21h13"}],["path",{d:"M3 9h11"}]],pp=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["rect",{width:"10",height:"8",x:"7",y:"8",rx:"1"}]],lp=[["path",{d:"M13.354 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14v6a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341l1.218-1.348"}],["path",{d:"M16 6h6"}],["path",{d:"M19 3v6"}]],A2=[["path",{d:"M12.531 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14v6a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341l.427-.473"}],["path",{d:"m16.5 3.5 5 5"}],["path",{d:"m21.5 3.5-5 5"}]],H2=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"}]],ip=[["path",{d:"M16.005 15.108a5.041 6.52 28.25 00-8.008-6.217 5.041 6.52 28.25 008.008 6.217A11.884 7.288-60.76 014.029 7.001"}],["path",{d:"M17 21h.01"}],["path",{d:"M7 3h.01"}],["path",{d:"M7.997 8.891a11.885 7.288-60.756 0111.977 8.107"}],["circle",{cx:"12",cy:"12",r:"1",fill:"currentColor"}]],np=[["path",{d:"M2 3v18"}],["rect",{width:"12",height:"18",x:"6",y:"3",rx:"2"}],["path",{d:"M22 3v18"}]],ep=[["path",{d:"M2 7v10"}],["path",{d:"M6 5v14"}],["rect",{width:"12",height:"18",x:"10",y:"3",rx:"2"}]],rp=[["rect",{width:"18",height:"14",x:"3",y:"3",rx:"2"}],["path",{d:"M4 21h1"}],["path",{d:"M9 21h1"}],["path",{d:"M14 21h1"}],["path",{d:"M19 21h1"}]],op=[["path",{d:"M7 2h10"}],["path",{d:"M5 6h14"}],["rect",{width:"18",height:"12",x:"3",y:"10",rx:"2"}]],vp=[["path",{d:"M3 2h18"}],["rect",{width:"18",height:"12",x:"3",y:"6",rx:"2"}],["path",{d:"M3 22h18"}]],$p=[["line",{x1:"6",x2:"10",y1:"11",y2:"11"}],["line",{x1:"8",x2:"8",y1:"9",y2:"13"}],["line",{x1:"15",x2:"15.01",y1:"12",y2:"12"}],["line",{x1:"18",x2:"18.01",y1:"10",y2:"10"}],["path",{d:"M17.32 5H6.68a4 4 0 0 0-3.978 3.59c-.006.052-.01.101-.017.152C2.604 9.416 2 14.456 2 16a3 3 0 0 0 3 3c1 0 1.5-.5 2-1l1.414-1.414A2 2 0 0 1 9.828 16h4.344a2 2 0 0 1 1.414.586L17 18c.5.5 1 1 2 1a3 3 0 0 0 3-3c0-1.545-.604-6.584-.685-7.258-.007-.05-.011-.1-.017-.151A4 4 0 0 0 17.32 5z"}]],mp=[["path",{d:"M11.146 15.854a1.207 1.207 0 0 1 1.708 0l1.56 1.56A2 2 0 0 1 15 18.828V21a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2.172a2 2 0 0 1 .586-1.414z"}],["path",{d:"M18.828 15a2 2 0 0 1-1.414-.586l-1.56-1.56a1.207 1.207 0 0 1 0-1.708l1.56-1.56A2 2 0 0 1 18.828 9H21a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1z"}],["path",{d:"M6.586 14.414A2 2 0 0 1 5.172 15H3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2.172a2 2 0 0 1 1.414.586l1.56 1.56a1.207 1.207 0 0 1 0 1.708z"}],["path",{d:"M9 3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2.172a2 2 0 0 1-.586 1.414l-1.56 1.56a1.207 1.207 0 0 1-1.708 0l-1.56-1.56A2 2 0 0 1 9 5.172z"}]],yp=[["line",{x1:"6",x2:"10",y1:"12",y2:"12"}],["line",{x1:"8",x2:"8",y1:"10",y2:"14"}],["line",{x1:"15",x2:"15.01",y1:"13",y2:"13"}],["line",{x1:"18",x2:"18.01",y1:"11",y2:"11"}],["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2"}]],sp=[["path",{d:"M12 2v2"}],["path",{d:"M12 8v2"}],["path",{d:"M12 14v2"}],["path",{d:"M12 20v2"}],["path",{d:"M21 3h-3a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3"}],["path",{d:"M3 3h3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3"}]],Cp=[["path",{d:"M2 12h2"}],["path",{d:"M8 12h2"}],["path",{d:"M14 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"M3 21v-3a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v3"}],["path",{d:"M3 3v3a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3"}]],up=[["path",{d:"m12 14 4-4"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0"}]],gp=[["path",{d:"m14 13-8.381 8.38a1 1 0 0 1-3.001-3l8.384-8.381"}],["path",{d:"m16 16 6-6"}],["path",{d:"m21.5 10.5-8-8"}],["path",{d:"m8 8 6-6"}],["path",{d:"m8.5 7.5 8 8"}]],Ap=[["path",{d:"M10.5 3 8 9l4 13 4-13-2.5-6"}],["path",{d:"M17 3a2 2 0 0 1 1.6.8l3 4a2 2 0 0 1 .013 2.382l-7.99 10.986a2 2 0 0 1-3.247 0l-7.99-10.986A2 2 0 0 1 2.4 7.8l2.998-3.997A2 2 0 0 1 7 3z"}],["path",{d:"M2 9h20"}]],Hp=[["path",{d:"m11 2 .925 1.848"}],["path",{d:"M13 15h.01"}],["path",{d:"M13.424 7.768a2 2 0 112.808 2.808"}],["path",{d:"m16 21-1-2.472"}],["path",{d:"M16.988 16.988A12 12 0 019 20a5 5 0 01-2.759-9.171 8.8 8.8 0 002.307-2.28"}],["path",{d:"m19 2-1 1.804"}],["path",{d:"m2 19 2.746-1.373"}],["path",{d:"m2 2 20 20"}],["path",{d:"m22 16-2.474-2.13a12 12 0 001.376-3.786 6 6 0 00-10.313-5.151"}],["path",{d:"m22 5-1.804 1"}],["path",{d:"m3 10 2 2"}],["path",{d:"M9 16h.01"}],["path",{d:"M9 20v2"}]],Vp=[["path",{d:"M11.5 21a7.5 7.5 0 1 1 7.35-9"}],["path",{d:"M13 12V3"}],["path",{d:"M4 21h16"}],["path",{d:"M9 12V3"}]],Sp=[["path",{d:"m11 2 .925 1.848"}],["path",{d:"M13 15h.01"}],["path",{d:"m16 21-1-2.472"}],["path",{d:"m19 2-1 1.804"}],["path",{d:"m2 19 2.746-1.373"}],["path",{d:"m22 16-2.474-2.13"}],["path",{d:"m22 5-1.804 1"}],["path",{d:"m3 10 2 2"}],["path",{d:"M9 16h.01"}],["path",{d:"M9 20v2"}],["path",{d:"M9.33 7.035c-.51 1.478-1.786 2.93-3.09 3.794A5 5 0 009 20a12.1 12.1 0 0011.902-9.916A6 6 0 009.33 7.035"}],["circle",{cx:"15",cy:"9",r:"2"}]],wp=[["path",{d:"M15 10v1"}],["path",{d:"M7.528 20.472a1.6 1.6 0 012.277 0l1.057 1.056a1.6 1.6 0 002.276 0l1.057-1.056a1.6 1.6 0 012.277 0l1.114 1.114a1.4 1.4 0 002.414-1V10a8 8 0 00-16 0v10.586a1.4 1.4 0 002.414 1z"}],["path",{d:"M9 10v1"}]],Lp=[["path",{d:"M12 7v14"}],["path",{d:"M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8"}],["path",{d:"M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5"}],["rect",{x:"3",y:"7",width:"18",height:"4",rx:"1"}]],fp=[["path",{d:"M15 6a9 9 0 0 0-9 9V3"}],["path",{d:"M21 18h-6"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}]],kp=[["path",{d:"M6 3v12"}],["path",{d:"M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"}],["path",{d:"M6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"}],["path",{d:"M15 6a9 9 0 0 0-9 9"}],["path",{d:"M18 15v6"}],["path",{d:"M21 18h-6"}]],Pp=[["path",{d:"M15 6a9 9 0 0 0-9 9V3"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}]],V2=[["circle",{cx:"12",cy:"12",r:"3"}],["line",{x1:"3",x2:"9",y1:"12",y2:"12"}],["line",{x1:"15",x2:"21",y1:"12",y2:"12"}]],Bp=[["path",{d:"M12 3v6"}],["circle",{cx:"12",cy:"12",r:"3"}],["path",{d:"M12 15v6"}]],zp=[["circle",{cx:"5",cy:"6",r:"3"}],["path",{d:"M12 6h5a2 2 0 0 1 2 2v7"}],["path",{d:"m15 9-3-3 3-3"}],["circle",{cx:"19",cy:"18",r:"3"}],["path",{d:"M12 18H7a2 2 0 0 1-2-2V9"}],["path",{d:"m9 15 3 3-3 3"}]],Dp=[["circle",{cx:"18",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M13 6h3a2 2 0 0 1 2 2v7"}],["path",{d:"M11 18H8a2 2 0 0 1-2-2V9"}]],Fp=[["circle",{cx:"12",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}],["circle",{cx:"18",cy:"6",r:"3"}],["path",{d:"M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9"}],["path",{d:"M12 12v3"}]],Rp=[["circle",{cx:"5",cy:"6",r:"3"}],["path",{d:"M5 9v6"}],["circle",{cx:"5",cy:"18",r:"3"}],["path",{d:"M12 3v18"}],["circle",{cx:"19",cy:"6",r:"3"}],["path",{d:"M16 15.7A9 9 0 0 0 19 9"}]],bp=[["path",{d:"M12 6h4a2 2 0 0 1 2 2v7"}],["path",{d:"M6 12v9"}],["path",{d:"m8.5 3.5-5 5"}],["path",{d:"m8.5 8.5-5-5"}],["circle",{cx:"18",cy:"18",r:"3"}]],Tp=[["circle",{cx:"18",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M6 21V9a9 9 0 0 0 9 9"}]],qp=[["circle",{cx:"5",cy:"6",r:"3"}],["path",{d:"M5 9v12"}],["circle",{cx:"19",cy:"18",r:"3"}],["path",{d:"m15 9-3-3 3-3"}],["path",{d:"M12 6h5a2 2 0 0 1 2 2v7"}]],Up=[["circle",{cx:"5",cy:"6",r:"3"}],["path",{d:"M5 9v12"}],["path",{d:"m15 9-3-3 3-3"}],["path",{d:"M12 6h5a2 2 0 0 1 2 2v3"}],["path",{d:"M19 15v6"}],["path",{d:"M22 18h-6"}]],Op=[["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M6 9v12"}],["path",{d:"M13 6h3a2 2 0 0 1 2 2v3"}],["path",{d:"M18 15v6"}],["path",{d:"M21 18h-6"}]],Gp=[["path",{d:"m15.5 3.5 5 5"}],["path",{d:"m15.5 8.5 5-5"}],["path",{d:"M18 11.62V15"}],["path",{d:"M6 9v12"}],["circle",{cx:"18",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}]],Zp=[["circle",{cx:"18",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M18 6V5"}],["path",{d:"M18 11v-1"}],["line",{x1:"6",x2:"6",y1:"9",y2:"21"}]],Ep=[["circle",{cx:"18",cy:"18",r:"3"}],["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M13 6h3a2 2 0 0 1 2 2v7"}],["line",{x1:"6",x2:"6",y1:"9",y2:"21"}]],Wp=[["path",{d:"M5.116 4.104A1 1 0 0 1 6.11 3h11.78a1 1 0 0 1 .994 1.105L17.19 20.21A2 2 0 0 1 15.2 22H8.8a2 2 0 0 1-2-1.79z"}],["path",{d:"M6 12a5 5 0 0 1 6 0 5 5 0 0 0 6 0"}]],Ip=[["circle",{cx:"6",cy:"15",r:"4"}],["circle",{cx:"18",cy:"15",r:"4"}],["path",{d:"M14 15a2 2 0 0 0-2-2 2 2 0 0 0-2 2"}],["path",{d:"M2.5 13 5 7c.7-1.3 1.4-2 3-2"}],["path",{d:"M21.5 13 19 7c-.7-1.3-1.5-2-3-2"}]],Xp=[["path",{d:"m15 6 2 2 4-4"}],["path",{d:"M2 12h20A10 10 0 1 1 12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 4-10"}]],Np=[["path",{d:"M15.5 10 13 7.5 15.5 5"}],["path",{d:"M15.861 14A14.5 14.5 0 0112 22a14.48 14.48 0 010-20 10 10 0 109.888 11.5"}],["path",{d:"M19.5 5 22 7.5 19.5 10"}],["path",{d:"M2 12h8.5"}]],Kp=[["path",{d:"M15.686 15A14.5 14.5 0 0 1 12 22a14.5 14.5 0 0 1 0-20 10 10 0 1 0 9.542 13"}],["path",{d:"M2 12h8.5"}],["path",{d:"M20 6V4a2 2 0 1 0-4 0v2"}],["rect",{width:"8",height:"5",x:"14",y:"6",rx:"1"}]],Jp=[["path",{d:"M10.114 4.462A14.5 14.5 0 0 1 12 2a10 10 0 0 1 9.313 13.643"}],["path",{d:"M15.557 15.556A14.5 14.5 0 0 1 12 22 10 10 0 0 1 4.929 4.929"}],["path",{d:"M15.892 10.234A14.5 14.5 0 0 0 12 2a10 10 0 0 0-3.643.687"}],["path",{d:"M17.656 12H22"}],["path",{d:"M19.071 19.071A10 10 0 0 1 12 22 14.5 14.5 0 0 1 8.44 8.45"}],["path",{d:"M2 12h10"}],["path",{d:"m2 2 20 20"}]],Qp=[["path",{d:"m16 3 5 5"}],["path",{d:"M2 12h20A10 10 0 1 1 12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 4-10"}],["path",{d:"m21 3-5 5"}]],jp=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["path",{d:"M2 12h20"}]],Yp=[["path",{d:"M12 13V2l8 4-8 4"}],["path",{d:"M20.561 10.222a9 9 0 1 1-12.55-5.29"}],["path",{d:"M8.002 9.997a5 5 0 1 0 8.9 2.02"}]],_p=[["path",{d:"M2 17h18a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H2"}],["path",{d:"M2 21V3"}],["path",{d:"M7 17v3a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1v-3"}],["circle",{cx:"16",cy:"11",r:"2"}],["circle",{cx:"8",cy:"11",r:"2"}]],xp=[["path",{d:"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z"}],["path",{d:"M22 10v6"}],["path",{d:"M6 12.5V16a6 3 0 0 0 12 0v-3.5"}]],al=[["path",{d:"M22 5V2l-5.89 5.89"}],["circle",{cx:"16.6",cy:"15.89",r:"3"}],["circle",{cx:"8.11",cy:"7.4",r:"3"}],["circle",{cx:"12.35",cy:"11.65",r:"3"}],["circle",{cx:"13.91",cy:"5.85",r:"3"}],["circle",{cx:"18.15",cy:"10.09",r:"3"}],["circle",{cx:"6.56",cy:"13.2",r:"3"}],["circle",{cx:"10.8",cy:"17.44",r:"3"}],["circle",{cx:"5",cy:"19",r:"3"}]],hl=[["circle",{cx:"12",cy:"7",r:"4"}],["circle",{cx:"4",cy:"19",r:"2"}],["circle",{cx:"12",cy:"19",r:"2"}],["circle",{cx:"20",cy:"19",r:"2"}],["path",{d:"M8 7a4 4 0 0 0-4 4v6"}],["path",{d:"M12 11v6"}],["path",{d:"M16 7a4 4 0 0 1 4 4v6"}]],S2=[["path",{d:"M12 3v17a1 1 0 0 1-1 1H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v6a1 1 0 0 1-1 1H3"}],["path",{d:"m16 19 2 2 4-4"}]],tl=[["path",{d:"m12.342 9.316 1.764-3.527"}],["path",{d:"m12.59 14.545 2.35 3.759"}],["path",{d:"m13.982 12.332 4.03.447"}],["path",{d:"M5.414 17.586 8.88 14.12"}],["path",{d:"M9.025 9.742 5.317 5.505"}],["circle",{cx:"11",cy:"12",r:"3"}],["circle",{cx:"15",cy:"4",r:"2"}],["circle",{cx:"16",cy:"20",r:"2"}],["circle",{cx:"20",cy:"13",r:"2"}],["circle",{cx:"4",cy:"19",r:"2"}],["circle",{cx:"4",cy:"4",r:"2"}]],w2=[["path",{d:"M12 3v17a1 1 0 0 1-1 1H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v6a1 1 0 0 1-1 1H3"}],["path",{d:"M16 19h6"}],["path",{d:"M19 22v-6"}]],L2=[["path",{d:"M12 3v17a1 1 0 0 1-1 1H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v6a1 1 0 0 1-1 1H3"}],["path",{d:"m16.5 16.5 5 5"}],["path",{d:"m16.5 21.5 5-5"}]],f2=[["path",{d:"M12 3v18"}],["path",{d:"M3 12h18"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],dl=[["path",{d:"M15 3v18"}],["path",{d:"M3 12h18"}],["path",{d:"M9 3v18"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],o=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M3 15h18"}],["path",{d:"M9 3v18"}],["path",{d:"M15 3v18"}]],cl=[["circle",{cx:"12",cy:"9",r:"1"}],["circle",{cx:"19",cy:"9",r:"1"}],["circle",{cx:"5",cy:"9",r:"1"}],["circle",{cx:"12",cy:"15",r:"1"}],["circle",{cx:"19",cy:"15",r:"1"}],["circle",{cx:"5",cy:"15",r:"1"}]],Ml=[["circle",{cx:"9",cy:"12",r:"1"}],["circle",{cx:"9",cy:"5",r:"1"}],["circle",{cx:"9",cy:"19",r:"1"}],["circle",{cx:"15",cy:"12",r:"1"}],["circle",{cx:"15",cy:"5",r:"1"}],["circle",{cx:"15",cy:"19",r:"1"}]],pl=[["circle",{cx:"12",cy:"5",r:"1"}],["circle",{cx:"19",cy:"5",r:"1"}],["circle",{cx:"5",cy:"5",r:"1"}],["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}],["circle",{cx:"12",cy:"19",r:"1"}],["circle",{cx:"19",cy:"19",r:"1"}],["circle",{cx:"5",cy:"19",r:"1"}]],ll=[["path",{d:"M10 15h4"}],["path",{d:"M19.424 11.095a2.5 2.5 0 00.471-2.309 2 2 0 00.424-3.286 2 2 0 00-2.143-3.322 2 2 0 00-.676.502 2 2 0 00-3.287.424 2.5 2.5 0 00-3.189 2.06A3 3 0 0012 11l4-4"}],["path",{d:"M4 12.006A1 1 0 014.994 11H19a1 1 0 011 1v7a2 2 0 01-2 2H6a2 2 0 01-2-2z"}],["path",{d:"M7 11a4 4 0 01-4-4V5a1 1 0 011-1h2a4 4 0 013.584 2.222"}]],il=[["path",{d:"M3 7V5c0-1.1.9-2 2-2h2"}],["path",{d:"M17 3h2c1.1 0 2 .9 2 2v2"}],["path",{d:"M21 17v2c0 1.1-.9 2-2 2h-2"}],["path",{d:"M7 21H5c-1.1 0-2-.9-2-2v-2"}],["rect",{width:"7",height:"5",x:"7",y:"7",rx:"1"}],["rect",{width:"7",height:"5",x:"10",y:"12",rx:"1"}]],nl=[["path",{d:"m11.9 12.1 4.514-4.514"}],["path",{d:"M20.1 2.3a1 1 0 0 0-1.4 0l-1.114 1.114A2 2 0 0 0 17 4.828v1.344a2 2 0 0 1-.586 1.414A2 2 0 0 1 17.828 7h1.344a2 2 0 0 0 1.414-.586L21.7 5.3a1 1 0 0 0 0-1.4z"}],["path",{d:"m6 16 2 2"}],["path",{d:"M8.23 9.85A3 3 0 0 1 11 8a5 5 0 0 1 5 5 3 3 0 0 1-1.85 2.77l-.92.38A2 2 0 0 0 12 18a4 4 0 0 1-4 4 6 6 0 0 1-6-6 4 4 0 0 1 4-4 2 2 0 0 0 1.85-1.23z"}]],el=[["path",{d:"m12.172 11.828 4.242-4.242"}],["path",{d:"M16.414 7.586A2 2 0 0017 6.172V5a2 2 0 011.177-1.823L20.59 2.09a1 1 0 011.32 1.32l-1.088 2.412A2 2 0 0119 7h-1a2 2 0 00-1.586.586"}],["path",{d:"m6 16 2 2"}],["path",{d:"M8.23 9.85a3 3 0 011.28-1.454 1 1 0 011.496.815 4 4 0 003.783 3.783 1 1 0 01.815 1.496 3 3 0 01-1.454 1.28l-.92.38A2 2 0 0012 18a4 4 0 01-4 4 6 6 0 01-6-6 4 4 0 014-4 2 2 0 001.85-1.23z"}]],k2=[["circle",{cx:"12",cy:"18",r:"4"}],["path",{d:"M16 18V2"}]],rl=[["path",{d:"M13.144 21.144A7.274 10.445 45 1 0 2.856 10.856"}],["path",{d:"M13.144 21.144A7.274 4.365 45 0 0 2.856 10.856a7.274 4.365 45 0 0 10.288 10.288"}],["path",{d:"M16.565 10.435 18.6 8.4a2.501 2.501 0 1 0 1.65-4.65 2.5 2.5 0 1 0-4.66 1.66l-2.024 2.025"}],["path",{d:"m8.5 16.5-1-1"}]],ol=[["path",{d:"M12 16H4a2 2 0 1 1 0-4h16a2 2 0 1 1 0 4h-4.25"}],["path",{d:"M5 12a2 2 0 0 1-2-2 9 7 0 0 1 18 0 2 2 0 0 1-2 2"}],["path",{d:"M5 16a2 2 0 0 0-2 2 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 2 2 0 0 0-2-2q0 0 0 0"}],["path",{d:"m6.67 12 6.13 4.6a2 2 0 0 0 2.8-.4l3.15-4.2"}]],vl=[["path",{d:"m15 12-9.373 9.373a1 1 0 0 1-3.001-3L12 9"}],["path",{d:"m18 15 4-4"}],["path",{d:"m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172v-.344a2 2 0 0 0-.586-1.414l-1.657-1.657A6 6 0 0 0 12.516 3H9l1.243 1.243A6 6 0 0 1 12 8.485V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5"}]],$l=[["path",{d:"M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17"}],["path",{d:"m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9"}],["path",{d:"m2 16 6 6"}],["circle",{cx:"16",cy:"9",r:"2.9"}],["circle",{cx:"6",cy:"5",r:"3"}]],ml=[["path",{d:"M12.035 17.012a3 3 0 0 0-3-3l-.311-.002a.72.72 0 0 1-.505-1.229l1.195-1.195A2 2 0 0 1 10.828 11H12a2 2 0 0 0 0-4H9.243a3 3 0 0 0-2.122.879l-2.707 2.707A4.83 4.83 0 0 0 3 14a8 8 0 0 0 8 8h2a8 8 0 0 0 8-8V7a2 2 0 1 0-4 0v2a2 2 0 1 0 4 0"}],["path",{d:"M13.888 9.662A2 2 0 0 0 17 8V5A2 2 0 1 0 13 5"}],["path",{d:"M9 5A2 2 0 1 0 5 5V10"}],["path",{d:"M9 7V4A2 2 0 1 1 13 4V7.268"}]],P2=[["path",{d:"M18 11.5V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1.4"}],["path",{d:"M14 10V8a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2"}],["path",{d:"M10 9.9V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v5"}],["path",{d:"M6 14a2 2 0 0 0-2-2a2 2 0 0 0-2 2"}],["path",{d:"M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-4a8 8 0 0 1-8-8 2 2 0 1 1 4 0"}]],yl=[["path",{d:"M11 14h2a2 2 0 0 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 16"}],["path",{d:"m14.45 13.39 5.05-4.694C20.196 8 21 6.85 21 5.75a2.75 2.75 0 0 0-4.797-1.837.276.276 0 0 1-.406 0A2.75 2.75 0 0 0 11 5.75c0 1.2.802 2.248 1.5 2.946L16 11.95"}],["path",{d:"m2 15 6 6"}],["path",{d:"m7 20 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a1 1 0 0 0-2.75-2.91"}]],B2=[["path",{d:"M11 12h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 14"}],["path",{d:"m7 18 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9"}],["path",{d:"m2 13 6 6"}]],sl=[["path",{d:"M18 12.5V10a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1.4"}],["path",{d:"M14 11V9a2 2 0 1 0-4 0v2"}],["path",{d:"M10 10.5V5a2 2 0 1 0-4 0v9"}],["path",{d:"m7 15-1.76-1.76a2 2 0 0 0-2.83 2.82l3.6 3.6C7.5 21.14 9.2 22 12 22h2a8 8 0 0 0 8-8V7a2 2 0 1 0-4 0v5"}]],Cl=[["path",{d:"M12 3V2"}],["path",{d:"m15.4 17.4 3.2-2.8a2 2 0 1 1 2.8 2.9l-3.6 3.3c-.7.8-1.7 1.2-2.8 1.2h-4c-1.1 0-2.1-.4-2.8-1.2l-1.302-1.464A1 1 0 0 0 6.151 19H5"}],["path",{d:"M2 14h12a2 2 0 0 1 0 4h-2"}],["path",{d:"M4 10h16"}],["path",{d:"M5 10a7 7 0 0 1 14 0"}],["path",{d:"M5 14v6a1 1 0 0 1-1 1H2"}]],ul=[["path",{d:"M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"}],["path",{d:"M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2"}],["path",{d:"M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"}],["path",{d:"M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"}]],gl=[["path",{d:"M2.048 18.566A2 2 0 0 0 4 21h16a2 2 0 0 0 1.952-2.434l-2-9A2 2 0 0 0 18 8H6a2 2 0 0 0-1.952 1.566z"}],["path",{d:"M8 11V6a4 4 0 0 1 8 0v5"}]],Al=[["path",{d:"M21 8 8 21"}],["path",{d:"m21 15-6 6"}]],Hl=[["path",{d:"m11 17 2 2a1 1 0 1 0 3-3"}],["path",{d:"m14 14 2.5 2.5a1 1 0 1 0 3-3l-3.88-3.88a3 3 0 0 0-4.24 0l-.88.88a1 1 0 1 1-3-3l2.81-2.81a5.79 5.79 0 0 1 7.06-.87l.47.28a2 2 0 0 0 1.42.25L21 4"}],["path",{d:"m21 3 1 11h-2"}],["path",{d:"M3 3 2 14l6.5 6.5a1 1 0 1 0 3-3"}],["path",{d:"M3 4h8"}]],Vl=[["path",{d:"M12 2v8"}],["path",{d:"m16 6-4 4-4-4"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2"}],["path",{d:"M6 18h.01"}],["path",{d:"M10 18h.01"}]],Sl=[["path",{d:"m16 6-4-4-4 4"}],["path",{d:"M12 2v8"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2"}],["path",{d:"M6 18h.01"}],["path",{d:"M10 18h.01"}]],wl=[["path",{d:"M10 16h.01"}],["path",{d:"M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"}],["path",{d:"M21.946 12.013H2.054"}],["path",{d:"M6 16h.01"}]],Ll=[["path",{d:"M10 10V5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v5"}],["path",{d:"M14 6a6 6 0 0 1 6 6v3"}],["path",{d:"M4 15v-3a6 6 0 0 1 6-6"}],["rect",{x:"2",y:"15",width:"20",height:"4",rx:"1"}]],fl=[["line",{x1:"4",x2:"20",y1:"9",y2:"9"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21"}]],kl=[["path",{d:"M14 18a2 2 0 0 0-4 0"}],["path",{d:"m19 11-2.11-6.657a2 2 0 0 0-2.752-1.148l-1.276.61A2 2 0 0 1 12 4H8.5a2 2 0 0 0-1.925 1.456L5 11"}],["path",{d:"M2 11h20"}],["circle",{cx:"17",cy:"18",r:"3"}],["circle",{cx:"7",cy:"18",r:"3"}]],Pl=[["path",{d:"M10 12H6"}],["path",{d:"M10 15V9"}],["path",{d:"M14 14.5a.5.5 0 0 0 .5.5h1a2.5 2.5 0 0 0 2.5-2.5v-1A2.5 2.5 0 0 0 15.5 9h-1a.5.5 0 0 0-.5.5z"}],["path",{d:"M6 15V9"}],["rect",{x:"2",y:"5",width:"20",height:"14",rx:"2"}]],Bl=[["path",{d:"m5.2 6.2 1.4 1.4"}],["path",{d:"M2 13h2"}],["path",{d:"M20 13h2"}],["path",{d:"m17.4 7.6 1.4-1.4"}],["path",{d:"M22 17H2"}],["path",{d:"M22 21H2"}],["path",{d:"M16 13a4 4 0 0 0-8 0"}],["path",{d:"M12 5V2.5"}]],zl=[["path",{d:"M22 9a1 1 0 00-1-1H3a1 1 0 00-1 1v4a1 1 0 001 1h.5a2 2 0 011.6.8l.3.4A2 2 0 007 16h10a2 2 0 001.6-.8l.3-.4a2 2 0 011.6-.8h.5a1 1 0 001-1z"}],["path",{d:"M8 12h8"}]],Dl=[["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}],["path",{d:"M12 18V6"}],["path",{d:"m17 12 3-2v8"}]],Fl=[["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}],["path",{d:"M12 18V6"}],["path",{d:"M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1"}]],Rl=[["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}],["path",{d:"M12 18V6"}],["path",{d:"M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2"}],["path",{d:"M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2"}]],bl=[["path",{d:"M12 18V6"}],["path",{d:"M17 10v3a1 1 0 0 0 1 1h3"}],["path",{d:"M21 10v8"}],["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}]],Tl=[["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}],["path",{d:"M12 18V6"}],["path",{d:"M17 13v-3h4"}],["path",{d:"M17 17.7c.4.2.8.3 1.3.3 1.5 0 2.7-1.1 2.7-2.5S19.8 13 18.3 13H17"}]],ql=[["path",{d:"M4 12h8"}],["path",{d:"M4 18V6"}],["path",{d:"M12 18V6"}],["circle",{cx:"19",cy:"16",r:"2"}],["path",{d:"M20 10c-2 2-3 3.5-3 6"}]],Ul=[["path",{d:"M6 12h12"}],["path",{d:"M6 20V4"}],["path",{d:"M18 20V4"}]],Ol=[["path",{d:"M21 14h-1.343"}],["path",{d:"M9.128 3.47A9 9 0 0 1 21 12v3.343"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20.414 20.414A2 2 0 0 1 19 21h-1a2 2 0 0 1-2-2v-3"}],["path",{d:"M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 2.636-6.364"}]],Gl=[["path",{d:"M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3"}]],Zl=[["path",{d:"M3 11h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5Zm0 0a9 9 0 1 1 18 0m0 0v5a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3Z"}],["path",{d:"M21 16v2a4 4 0 0 1-4 4h-5"}]],El=[["path",{d:"M12.409 5.824c-.702.792-1.15 1.496-1.415 2.166l2.153 2.156a.5.5 0 0 1 0 .707l-2.293 2.293a.5.5 0 0 0 0 .707L12 15"}],["path",{d:"M13.508 20.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5a5.5 5.5 0 0 1 9.591-3.677.6.6 0 0 0 .818.001A5.5 5.5 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5z"}]],Wl=[["path",{d:"M19.414 14.414C21 12.828 22 11.5 22 9.5a5.5 5.5 0 0 0-9.591-3.676.6.6 0 0 1-.818.001A5.5 5.5 0 0 0 2 9.5c0 2.3 1.5 4 3 5.5l5.535 5.362a2 2 0 0 0 2.879.052 2.12 2.12 0 0 0-.004-3 2.124 2.124 0 1 0 3-3 2.124 2.124 0 0 0 3.004 0 2 2 0 0 0 0-2.828l-1.881-1.882a2.41 2.41 0 0 0-3.409 0l-1.71 1.71a2 2 0 0 1-2.828 0 2 2 0 0 1 0-2.828l2.823-2.762"}]],Il=[["path",{d:"m14.876 18.99-1.368 1.323a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5a5.2 5.2 0 0 1-.244 1.572"}],["path",{d:"M15 15h6"}]],Xl=[["path",{d:"M10.5 4.893a5.5 5.5 0 0 1 1.091.931.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 1.872-1.002 3.356-2.187 4.655"}],["path",{d:"m16.967 16.967-3.459 3.346a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5a5.5 5.5 0 0 1 2.747-4.761"}],["path",{d:"m2 2 20 20"}]],Nl=[["path",{d:"m14.479 19.374-.971.939a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5a5.2 5.2 0 0 1-.219 1.49"}],["path",{d:"M15 15h6"}],["path",{d:"M18 12v6"}]],Kl=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"}],["path",{d:"M3.22 13H9.5l.5-1 2 4.5 2-7 1.5 3.5h5.27"}]],Jl=[["path",{d:"m15.5 12.5 5 5"}],["path",{d:"m20.5 12.5-5 5"}],["path",{d:"M21.955 8.774a5.5 5.5 0 0 0-9.546-2.95.6.6 0 0 1-.818 0A5.5 5.5 0 0 0 2 9.5c0 2.3 1.5 4 3 5.5l5.508 5.332a2 2 0 0 0 2.57.352"}]],Ql=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"}]],jl=[["path",{d:"M11 8c2-3-2-3 0-6"}],["path",{d:"M15.5 8c2-3-2-3 0-6"}],["path",{d:"M6 10h.01"}],["path",{d:"M6 14h.01"}],["path",{d:"M10 16v-4"}],["path",{d:"M14 16v-4"}],["path",{d:"M18 16v-4"}],["path",{d:"M20 6a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3"}],["path",{d:"M5 20v2"}],["path",{d:"M19 20v2"}]],Yl=[["path",{d:"M11 17v4"}],["path",{d:"M14 3v8a2 2 0 0 0 2 2h5.865"}],["path",{d:"M17 17v4"}],["path",{d:"M18 17a4 4 0 0 0 4-4 8 6 0 0 0-8-6 6 5 0 0 0-6 5v3a2 2 0 0 0 2 2z"}],["path",{d:"M2 10v5"}],["path",{d:"M6 3h16"}],["path",{d:"M7 21h14"}],["path",{d:"M8 13H2"}]],_l=[["path",{d:"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"}]],xl=[["path",{d:"m9 11-6 6v3h9l3-3"}],["path",{d:"m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"}]],ai=[["path",{d:"M13.5 10.5 2 22"}],["path",{d:"M16.352 11.648a1.205 1.205 0 01-1.704 0l-2.296-2.296a1.205 1.205 0 010-1.704l.72-.72a2 2 0 011.022-.546l.599-.12a2 2 0 001.569-1.57l.12-.598a2 2 0 01.546-1.022l.72-.72a1.205 1.205 0 011.704 0l2.296 2.296a1.205 1.205 0 010 1.704l-6.02 6.02a1 1 0 103 3l.201-.201A7.4 7.4 0 0021 9.93V7"}],["path",{d:"m6 21-3-3"}]],hi=[["path",{d:"M10.82 16.12c1.69.6 3.91.79 5.18.85.55.03 1-.42.97-.97-.06-1.27-.26-3.5-.85-5.18"}],["path",{d:"M11.5 6.5c1.64 0 5-.38 6.71-1.07.52-.2.55-.82.12-1.17A10 10 0 0 0 4.26 18.33c.35.43.96.4 1.17-.12.69-1.71 1.07-5.07 1.07-6.71 1.34.45 3.1.9 4.88.62a.88.88 0 0 0 .73-.74c.3-2.14-.15-3.5-.61-4.88"}],["path",{d:"M15.62 16.95c.2.85.62 2.76.5 4.28a.77.77 0 0 1-.9.7 16.64 16.64 0 0 1-4.08-1.36"}],["path",{d:"M16.13 21.05c1.65.63 3.68.84 4.87.91a.9.9 0 0 0 .96-.96 17.68 17.68 0 0 0-.9-4.87"}],["path",{d:"M16.94 15.62c.86.2 2.77.62 4.29.5a.77.77 0 0 0 .7-.9 16.64 16.64 0 0 0-1.36-4.08"}],["path",{d:"M17.99 5.52a20.82 20.82 0 0 1 3.15 4.5.8.8 0 0 1-.68 1.13c-2.33.2-5.3-.32-8.27-1.57"}],["path",{d:"M4.93 4.93 3 3a.7.7 0 0 1 0-1"}],["path",{d:"M9.58 12.18c1.24 2.98 1.77 5.95 1.57 8.28a.8.8 0 0 1-1.13.68 20.82 20.82 0 0 1-4.5-3.15"}]],ti=[["path",{d:"M10.82 16.12c1.69.6 3.91.79 5.18.85.28.01.53-.09.7-.27"}],["path",{d:"M11.14 20.57c.52.24 2.44 1.12 4.08 1.37.46.06.86-.25.9-.71.12-1.52-.3-3.43-.5-4.28"}],["path",{d:"M16.13 21.05c1.65.63 3.68.84 4.87.91a.9.9 0 0 0 .7-.26"}],["path",{d:"M17.99 5.52a20.83 20.83 0 0 1 3.15 4.5.8.8 0 0 1-.68 1.13c-1.17.1-2.5.02-3.9-.25"}],["path",{d:"M20.57 11.14c.24.52 1.12 2.44 1.37 4.08.04.3-.08.59-.31.75"}],["path",{d:"M4.93 4.93a10 10 0 0 0-.67 13.4c.35.43.96.4 1.17-.12.69-1.71 1.07-5.07 1.07-6.71 1.34.45 3.1.9 4.88.62a.85.85 0 0 0 .48-.24"}],["path",{d:"M5.52 17.99c1.05.95 2.91 2.42 4.5 3.15a.8.8 0 0 0 1.13-.68c.2-2.34-.33-5.3-1.57-8.28"}],["path",{d:"M8.35 2.68a10 10 0 0 1 9.98 1.58c.43.35.4.96-.12 1.17-1.5.6-4.3.98-6.07 1.05"}],["path",{d:"m2 2 20 20"}]],di=[["path",{d:"M12 7v4"}],["path",{d:"M14 21v-3a2 2 0 0 0-4 0v3"}],["path",{d:"M14 9h-4"}],["path",{d:"M18 11h2a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h2"}],["path",{d:"M18 21V5a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16"}]],ci=[["path",{d:"M10 22v-6.57"}],["path",{d:"M12 11h.01"}],["path",{d:"M12 7h.01"}],["path",{d:"M14 15.43V22"}],["path",{d:"M15 16a5 5 0 0 0-6 0"}],["path",{d:"M16 11h.01"}],["path",{d:"M16 7h.01"}],["path",{d:"M8 11h.01"}],["path",{d:"M8 7h.01"}],["rect",{x:"4",y:"2",width:"16",height:"20",rx:"2"}]],Mi=[["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.228 16.852-.923-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"M17 2v4.172a2 2 0 0 1-.586 1.414l-8.828 8.828A2 2 0 0 0 7 17.828V22"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"m20.772 16.852.924-.383"}],["path",{d:"m20.772 19.148.924.383"}],["path",{d:"M5 22h6.159"}],["path",{d:"M5 2h14"}],["path",{d:"M7 2v4.172a2 2 0 0 0 .586 1.414l5.188 5.188"}],["circle",{cx:"18",cy:"18",r:"3"}]],pi=[["path",{d:"M5 22h14"}],["path",{d:"M5 2h14"}],["path",{d:"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"}],["path",{d:"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"}]],li=[["path",{d:"M10.584 21H5a2 2 0 01-2-2v-9a2 2 0 01.709-1.527l7-6a2 2 0 012.582 0l7 6A2 2 0 0121 10.001v.583"}],["path",{d:"M14 12H10a1 1 0 00-1 1v8"}],["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.229 16.852-.924-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.773-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.697-.382-.924"}],["path",{d:"m20.773 16.852.922-.383"}],["path",{d:"m20.773 19.148.922.383"}],["circle",{cx:"18",cy:"18",r:"3"}]],ii=[["path",{d:"M8.62 13.8A2.25 2.25 0 1 1 12 10.836a2.25 2.25 0 1 1 3.38 2.966l-2.626 2.856a.998.998 0 0 1-1.507 0z"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"}]],ni=[["path",{d:"M12.35 21H5a2 2 0 0 1-2-2v-9a2 2 0 0 1 .71-1.53l7-6a2 2 0 0 1 2.58 0l7 6A2 2 0 0 1 21 10v2.35"}],["path",{d:"M14.8 12.4A1 1 0 0 0 14 12h-4a1 1 0 0 0-1 1v8"}],["path",{d:"M15 18h6"}],["path",{d:"M18 15v6"}]],ei=[["path",{d:"M10 12V8.964"}],["path",{d:"M14 12V8.964"}],["path",{d:"M15 12a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2a1 1 0 0 1 1-1z"}],["path",{d:"M8.5 21H5a2 2 0 0 1-2-2v-9a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-2"}]],ri=[["path",{d:"M9.5 13.866a4 4 0 0 1 5 .01"}],["path",{d:"M12 17h.01"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"}],["path",{d:"M7 10.754a8 8 0 0 1 10 0"}]],z2=[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"}]],oi=[["path",{d:"m12.681 4.24.834-.715a1.45 1.45 0 011.88 0l5.09 4.364A1.45 1.45 0 0121 9v6.546a1.45 1.45 0 01-1 1.381"}],["path",{d:"M15.485 11.889A1.45 1.45 0 0116 13v6.546A1.454 1.454 0 0114.546 21H4.364a1.454 1.454 0 01-1.454-1.454V13a1.45 1.45 0 01.515-1.111l5.09-4.364a1.45 1.45 0 011.88 0z"}],["path",{d:"M7.41 20.546v-4a1 1 0 011-1h2a1 1 0 011 1v4"}]],D2=[["path",{d:"M12 17c5 0 8-2.69 8-6H4c0 3.31 3 6 8 6m-4 4h8m-4-3v3M5.14 11a3.5 3.5 0 1 1 6.71 0"}],["path",{d:"M12.14 11a3.5 3.5 0 1 1 6.71 0"}],["path",{d:"M15.5 6.5a3.5 3.5 0 1 0-7 0"}]],F2=[["path",{d:"m7 11 4.08 10.35a1 1 0 0 0 1.84 0L17 11"}],["path",{d:"M17 7A5 5 0 0 0 7 7"}],["path",{d:"M17 7a2 2 0 0 1 0 4H7a2 2 0 0 1 0-4"}]],vi=[["path",{d:"M13.5 8h-3"}],["path",{d:"m15 2-1 2h3a2 2 0 012 2v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6a2 2 0 012-2h3"}],["path",{d:"M16 22a4 4 0 00-8 0"}],["path",{d:"m9 2 3 6"}],["circle",{cx:"12",cy:"15",r:"3"}]],$i=[["path",{d:"M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21"}],["path",{d:"m14 19 3 3v-5.5"}],["path",{d:"m17 22 3-3"}],["circle",{cx:"9",cy:"9",r:"2"}]],mi=[["path",{d:"M21 9v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"}],["line",{x1:"16",x2:"22",y1:"5",y2:"5"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]],yi=[["line",{x1:"2",x2:"22",y1:"2",y2:"22"}],["path",{d:"M10.41 10.41a2 2 0 1 1-2.83-2.83"}],["line",{x1:"13.5",x2:"6",y1:"13.5",y2:"21"}],["line",{x1:"18",x2:"21",y1:"12",y2:"15"}],["path",{d:"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59"}],["path",{d:"M21 15V5a2 2 0 0 0-2-2H9"}]],si=[["path",{d:"M13 19a4 4 0 00-8 0"}],["path",{d:"M16 10h2"}],["path",{d:"M16 14h2"}],["circle",{cx:"9",cy:"12",r:"3"}],["rect",{x:"2",y:"5",width:"20",height:"14",rx:"2"}]],Ci=[["path",{d:"M15 15.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997a1 1 0 0 1-1.517-.86z"}],["path",{d:"M21 12.17V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"}],["path",{d:"m6 21 5-5"}],["circle",{cx:"9",cy:"9",r:"2"}]],ui=[["path",{d:"M16 5h6"}],["path",{d:"M19 2v6"}],["path",{d:"M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}],["circle",{cx:"9",cy:"9",r:"2"}]],gi=[["path",{d:"M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21"}],["path",{d:"m14 19.5 3-3 3 3"}],["path",{d:"M17 22v-5.5"}],["circle",{cx:"9",cy:"9",r:"2"}]],Ai=[["path",{d:"M16 3h5v5"}],["path",{d:"M17 21h2a2 2 0 0 0 2-2"}],["path",{d:"M21 12v3"}],["path",{d:"m21 3-5 5"}],["path",{d:"M3 7V5a2 2 0 0 1 2-2"}],["path",{d:"m5 21 4.144-4.144a1.21 1.21 0 0 1 1.712 0L13 19"}],["path",{d:"M9 3h3"}],["rect",{x:"3",y:"11",width:"10",height:"10",rx:"1"}]],Hi=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]],Vi=[["path",{d:"m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16"}],["path",{d:"M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2"}],["circle",{cx:"13",cy:"7",r:"1",fill:"currentColor"}],["rect",{x:"8",y:"2",width:"14",height:"14",rx:"2"}]],Si=[["path",{d:"M12 3v12"}],["path",{d:"m8 11 4 4 4-4"}],["path",{d:"M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4"}]],wi=[["path",{d:"M6 3h12"}],["path",{d:"M6 8h12"}],["path",{d:"m6 13 8.5 8"}],["path",{d:"M6 13h3"}],["path",{d:"M9 13c6.667 0 6.667-10 0-10"}]],Li=[["path",{d:"M6 16c5 0 7-8 12-8a4 4 0 0 1 0 8c-5 0-7-8-12-8a4 4 0 1 0 0 8"}]],fi=[["polyline",{points:"22 12 16 12 14 15 10 15 8 12 2 12"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"}]],ki=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 16v-4"}],["path",{d:"M12 8h.01"}]],Pi=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 7h.01"}],["path",{d:"M17 7h.01"}],["path",{d:"M7 17h.01"}],["path",{d:"M17 17h.01"}]],Bi=[["line",{x1:"19",x2:"10",y1:"4",y2:"4"}],["line",{x1:"14",x2:"5",y1:"20",y2:"20"}],["line",{x1:"15",x2:"9",y1:"4",y2:"20"}]],zi=[["path",{d:"m16 14 4 4-4 4"}],["path",{d:"M20 10a8 8 0 1 0-8 8h8"}]],Di=[["path",{d:"M12 18v2a2 2 0 002 2h6"}],["path",{d:"M6 11c.72.5 1.44 1 3 1 3 0 3-2 6-2 1.56 0 2.28.5 3 1"}],["path",{d:"M9.293 3c.453 0 .887-.18 1.207-.5s.754-.5 1.207-.5h.586c.453 0 .887.18 1.207.5s.754.5 1.207.5H16a2 2 0 012 2v11a2 2 0 01-2 2H8a2 2 0 01-2-2V5a2 2 0 012-2z"}]],Fi=[["path",{d:"M4 10a8 8 0 1 1 8 8H4"}],["path",{d:"m8 22-4-4 4-4"}]],Ri=[["path",{d:"M12 9.5V21m0-11.5L6 3m6 6.5L18 3"}],["path",{d:"M6 15h12"}],["path",{d:"M6 11h12"}]],bi=[["path",{d:"M21 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2Z"}],["path",{d:"M6 15v-2"}],["path",{d:"M12 15V9"}],["circle",{cx:"12",cy:"6",r:"3"}]],Ti=[["path",{d:"M18 17a1 1 0 0 0-1 1v1a2 2 0 1 0 2-2z"}],["path",{d:"M20.97 3.61a.45.45 0 0 0-.58-.58C10.2 6.6 6.6 10.2 3.03 20.39a.45.45 0 0 0 .58.58C13.8 17.4 17.4 13.8 20.97 3.61"}],["path",{d:"m6.707 6.707 10.586 10.586"}],["path",{d:"M7 5a2 2 0 1 0-2 2h1a1 1 0 0 0 1-1z"}]],qi=[["path",{d:"M5 3v14"}],["path",{d:"M12 3v8"}],["path",{d:"M19 3v18"}]],Ui=[["path",{d:"M12 8v12"}],["path",{d:"M6 4h12"}],["path",{d:"M6 8h12"}]],Oi=[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor"}]],Gi=[["path",{d:"M12.4 2.7a2.5 2.5 0 0 1 3.4 0l5.5 5.5a2.5 2.5 0 0 1 0 3.4l-3.7 3.7a2.5 2.5 0 0 1-3.4 0L8.7 9.8a2.5 2.5 0 0 1 0-3.4z"}],["path",{d:"m14 7 3 3"}],["path",{d:"m9.4 10.6-6.814 6.814A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814"}]],Zi=[["path",{d:"m2 21 9.6-9.6"}],["path",{d:"m7.5 15.5 2.3 2.3a1 1 0 0 1 0 1.4l-2.1 2.1a1 1 0 0 1-1.4 0L4 19"}],["circle",{cx:"15.5",cy:"7.5",r:"5.5"}]],Ei=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M6 8h4"}],["path",{d:"M14 8h.01"}],["path",{d:"M18 8h.01"}],["path",{d:"M2 12h20"}],["path",{d:"M6 12v4"}],["path",{d:"M10 12v4"}],["path",{d:"M14 12v4"}],["path",{d:"M18 12v4"}]],Wi=[["path",{d:"M10 8h.01"}],["path",{d:"M12 12h.01"}],["path",{d:"M14 8h.01"}],["path",{d:"M16 12h.01"}],["path",{d:"M18 8h.01"}],["path",{d:"M6 8h.01"}],["path",{d:"M7 16h10"}],["path",{d:"M8 12h.01"}],["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}]],Ii=[["path",{d:"M 20 4 A2 2 0 0 1 22 6"}],["path",{d:"M 22 6 L 22 16.41"}],["path",{d:"M 7 16 L 16 16"}],["path",{d:"M 9.69 4 L 20 4"}],["path",{d:"M14 8h.01"}],["path",{d:"M18 8h.01"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20 20H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2"}],["path",{d:"M6 8h.01"}],["path",{d:"M8 12h.01"}]],Xi=[["path",{d:"M11.38 10 5 20"}],["path",{d:"M19 18a2 2 0 01-2 2c-4.87-.003-5.052-16-10-16a2 2 0 00-2 2"}]],Ni=[["path",{d:"M12 2v5"}],["path",{d:"M14.829 15.998a3 3 0 1 1-5.658 0"}],["path",{d:"M20.92 14.606A1 1 0 0 1 20 16H4a1 1 0 0 1-.92-1.394l3-7A1 1 0 0 1 7 7h10a1 1 0 0 1 .92.606z"}]],Ki=[["path",{d:"M10.293 2.293a1 1 0 0 1 1.414 0l2.5 2.5 5.994 1.227a1 1 0 0 1 .506 1.687l-7 7a1 1 0 0 1-1.687-.506l-1.227-5.994-2.5-2.5a1 1 0 0 1 0-1.414z"}],["path",{d:"m14.207 4.793-3.414 3.414"}],["path",{d:"M3 20a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z"}],["path",{d:"m9.086 6.5-4.793 4.793a1 1 0 0 0-.18 1.17L7 18"}]],Ji=[["path",{d:"M12 10v12"}],["path",{d:"M17.929 7.629A1 1 0 0 1 17 9H7a1 1 0 0 1-.928-1.371l2-5A1 1 0 0 1 9 2h6a1 1 0 0 1 .928.629z"}],["path",{d:"M9 22h6"}]],Qi=[["path",{d:"M19.929 18.629A1 1 0 0 1 19 20H9a1 1 0 0 1-.928-1.371l2-5A1 1 0 0 1 11 13h6a1 1 0 0 1 .928.629z"}],["path",{d:"M6 3a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"}],["path",{d:"M8 6h4a2 2 0 0 1 2 2v5"}]],ji=[["path",{d:"M19.929 9.629A1 1 0 0 1 19 11H9a1 1 0 0 1-.928-1.371l2-5A1 1 0 0 1 11 4h6a1 1 0 0 1 .928.629z"}],["path",{d:"M6 15a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1z"}],["path",{d:"M8 18h4a2 2 0 0 0 2-2v-5"}]],Yi=[["path",{d:"M12 12v6"}],["path",{d:"M4.077 10.615A1 1 0 0 0 5 12h14a1 1 0 0 0 .923-1.385l-3.077-7.384A2 2 0 0 0 15 2H9a2 2 0 0 0-1.846 1.23Z"}],["path",{d:"M8 20a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1z"}]],_i=[["path",{d:"m12 8 6-3-6-3v10"}],["path",{d:"m8 11.99-5.5 3.14a1 1 0 0 0 0 1.74l8.5 4.86a2 2 0 0 0 2 0l8.5-4.86a1 1 0 0 0 0-1.74L16 12"}],["path",{d:"m6.49 12.85 11.02 6.3"}],["path",{d:"M17.51 12.85 6.5 19.15"}]],xi=[["path",{d:"M10 18v-7"}],["path",{d:"M11.119 2.205a2 2 0 0 1 1.762 0l7.84 3.846A.5.5 0 0 1 20.5 7h-17a.5.5 0 0 1-.22-.949z"}],["path",{d:"M14 18v-7"}],["path",{d:"M18 18v-7"}],["path",{d:"M3 22h18"}],["path",{d:"M6 18v-7"}]],an=[["path",{d:"m5 8 6 6"}],["path",{d:"m4 14 6-6 2-3"}],["path",{d:"M2 5h12"}],["path",{d:"M7 2h1"}],["path",{d:"m22 22-5-10-5 10"}],["path",{d:"M14 18h6"}]],hn=[["path",{d:"M2 20h20"}],["path",{d:"m9 10 2 2 4-4"}],["rect",{x:"3",y:"4",width:"18",height:"12",rx:"2"}]],R2=[["rect",{width:"18",height:"12",x:"3",y:"4",rx:"2",ry:"2"}],["line",{x1:"2",x2:"22",y1:"20",y2:"20"}]],tn=[["path",{d:"M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"}],["path",{d:"M20.054 15.987H3.946"}]],dn=[["path",{d:"M7 22a5 5 0 0 1-2-4"}],["path",{d:"M7 16.93c.96.43 1.96.74 2.99.91"}],["path",{d:"M3.34 14A6.8 6.8 0 0 1 2 10c0-4.42 4.48-8 10-8s10 3.58 10 8a7.19 7.19 0 0 1-.33 2"}],["path",{d:"M5 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"}],["path",{d:"M14.33 22h-.09a.35.35 0 0 1-.24-.32v-10a.34.34 0 0 1 .33-.34c.08 0 .15.03.21.08l7.34 6a.33.33 0 0 1-.21.59h-4.49l-2.57 3.85a.35.35 0 0 1-.28.14z"}]],cn=[["path",{d:"M12 10v10"}],["path",{d:"M22 10a1 1 0 01-.59.92l-5.077 2.308"}],["path",{d:"M22.017 10.005a1 1 0 00-.597-.916l-8.59-3.91a2 2 0 00-1.66.001L2.6 9.08a1 1 0 00-.02 1.831l5.093 2.316"}],["path",{d:"m9 17 3 3 3-3"}]],Mn=[["path",{d:"M3.704 14.467a10 8 0 1 1 3.115 2.375"}],["path",{d:"M7 22a5 5 0 0 1-2-3.994"}],["circle",{cx:"5",cy:"16",r:"2"}]],pn=[["path",{d:"M12 14V4"}],["path",{d:"M7.674 10.774 2.58 13.09a1 1 0 000 1.822l8.6 3.91a2 2 0 001.65 0l8.58-3.9a1 1 0 00.59-.92 1 1 0 00-.59-.922l-5.078-2.308"}],["path",{d:"m9 7 3-3 3 3"}]],ln=[["path",{d:"M13 13.74a2 2 0 0 1-2 0L2.5 8.87a1 1 0 0 1 0-1.74L11 2.26a2 2 0 0 1 2 0l8.5 4.87a1 1 0 0 1 0 1.74z"}],["path",{d:"m20 14.285 1.5.845a1 1 0 0 1 0 1.74L13 21.74a2 2 0 0 1-2 0l-8.5-4.87a1 1 0 0 1 0-1.74l1.5-.845"}]],nn=[["path",{d:"M12 7v15"}],["path",{d:"M2 12a1 1 0 00.58.91l5.093 2.316"}],["path",{d:"M22 12a1 1 0 01-.59.92l-5.077 2.308"}],["path",{d:"M8 10.37 2.6 7.91a1 1 0 010-1.831l8.57-3.9a2 2 0 011.66.001l8.59 3.91a1 1 0 010 1.831l-5.392 2.45"}],["path",{d:"m9 19 3 3 3-3"}]],en=[["path",{d:"M12 12V2"}],["path",{d:"M2 17.002a1 1 0 00.58.91l8.6 3.91a2 2 0 001.65 0l8.58-3.9a1 1 0 00.59-.92"}],["path",{d:"M7.674 8.774 2.58 11.09a1 1 0 000 1.822l8.6 3.91a2 2 0 001.65 0l8.58-3.9a1 1 0 00.59-.92 1 1 0 00-.59-.922l-5.078-2.308"}],["path",{d:"m9 5 3-3 3 3"}]],rn=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l8.58-3.9a1 1 0 0 0 0-1.832z"}],["path",{d:"M16 17h6"}],["path",{d:"M2.003 11.995a1 1 0 0 0 .597.915l8.58 3.91a2 2 0 0 0 .83.18"}],["path",{d:"M2.003 16.995a1 1 0 0 0 .597.915l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l2.11-.96"}],["path",{d:"M22.018 12.004a1 1 0 0 1-.598.916l-.177.08"}]],on=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l8.58-3.9a1 1 0 0 0 0-1.831z"}],["path",{d:"M16 17h6"}],["path",{d:"M19 14v6"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 .825.178"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l2.116-.962"}]],b2=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"}]],vn=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1"}],["path",{d:"M18 3v18"}],["path",{d:"m21 18-3 3-3-3"}]],$n=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1"}],["path",{d:"M3 18h18"}],["path",{d:"m18 21 3-3-3-3"}]],mn=[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1"}]],yn=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"4",rx:"1"}],["rect",{width:"7",height:"7",x:"4",y:"14",rx:"1"}]],sn=[["circle",{cx:"17.5",cy:"17.5",r:"3.5"}],["circle",{cx:"17.5",cy:"6.5",r:"3.5"}],["circle",{cx:"6.5",cy:"17.5",r:"3.5"}],["circle",{cx:"6.5",cy:"6.5",r:"3.5"}]],Cn=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1"}],["path",{d:"M14 4h7"}],["path",{d:"M14 9h7"}],["path",{d:"M14 15h7"}],["path",{d:"M14 20h7"}]],un=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1"}]],gn=[["rect",{width:"7",height:"18",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1"}]],An=[["rect",{width:"18",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1"}]],Hn=[["rect",{width:"18",height:"7",x:"3",y:"3",rx:"1"}],["rect",{width:"9",height:"7",x:"3",y:"14",rx:"1"}],["rect",{width:"5",height:"7",x:"16",y:"14",rx:"1"}]],Vn=[["path",{d:"M11 20a10 10 0 0010-10 25.9 25.9 0 00-1.04-7.281 1 1 0 00-1.755-.325C15.833 5.5 13 5.5 9.8 6.1A7 7 0 0011 20"}],["path",{d:"M2 21a5 5 0 012.911-4.544C7.613 15.212 8.351 15.24 11 13"}]],Sn=[["path",{d:"M2 22c1.25-.987 2.27-1.975 3.9-2.2a5.56 5.56 0 0 1 3.8 1.5 4 4 0 0 0 6.187-2.353 3.5 3.5 0 0 0 3.69-5.116A3.5 3.5 0 0 0 20.95 8 3.5 3.5 0 1 0 16 3.05a3.5 3.5 0 0 0-5.831 1.373 3.5 3.5 0 0 0-5.116 3.69 4 4 0 0 0-2.348 6.155C3.499 15.42 4.409 16.712 4.2 18.1 3.926 19.743 3.014 20.732 2 22"}],["path",{d:"M2 22 17 7"}]],wn=[["path",{d:"M15 13h4a2 2 0 001.901-1.38l1.057-4.333A1 1 0 0021 6H3a1 1 0 00-.958 1.287L3.1 11.621A2 2 0 005.001 13h4"}],["path",{d:"M15 22V11a1 1 0 00-1-1h-4a1 1 0 00-1 1v11"}],["path",{d:"M18 22H6"}],["path",{d:"M18 6V3a1 1 0 00-1-1h-3"}]],Ln=[["path",{d:"M7 2a1 1 0 0 0-.8 1.6 14 14 0 0 1 0 16.8A1 1 0 0 0 7 22h10a1 1 0 0 0 .8-1.6 14 14 0 0 1 0-16.8A1 1 0 0 0 17 2z"}]],fn=[["path",{d:"M13.433 2a1 1 0 0 1 .824.448 18 18 0 0 1 0 19.104 1 1 0 0 1-.824.448h-2.866a1 1 0 0 1-.824-.448 18 18 0 0 1 0-19.104A1 1 0 0 1 10.567 2z"}]],kn=[["path",{d:"M15.725 7.296h.01"}],["path",{d:"M6 12a6 6 0 016-6"}],["circle",{cx:"12",cy:"12",r:"10"}]],Pn=[["path",{d:"M15 14H9"}],["path",{d:"M21 3v18"}],["path",{d:"M3 3v18"}],["path",{d:"m8 16 3.453-7.648a.6.6 0 011.094.001L16 16"}]],Bn=[["path",{d:"M15 8H9"}],["path",{d:"M21 15.354a4 4 0 100 5.292"}],["path",{d:"M3 18h4a2 2 0 010 4H3.5a.5.5 0 01-.5-.5v-7a.5.5 0 01.5-.5H6a2 2 0 010 4"}],["path",{d:"m8 10 3.453-7.648a.6.6 0 011.094 0L16 10"}]],zn=[["rect",{width:"8",height:"18",x:"3",y:"3",rx:"1"}],["path",{d:"M7 3v18"}],["path",{d:"M20.4 18.9c.2.5-.1 1.1-.6 1.3l-1.9.7c-.5.2-1.1-.1-1.3-.6L11.1 5.1c-.2-.5.1-1.1.6-1.3l1.9-.7c.5-.2 1.1.1 1.3.6Z"}]],Dn=[["path",{d:"m16 6 4 14"}],["path",{d:"M12 6v14"}],["path",{d:"M8 8v12"}],["path",{d:"M4 4v16"}]],Fn=[["path",{d:"M14 12h2v8"}],["path",{d:"M14 20h4"}],["path",{d:"M6 12h4"}],["path",{d:"M6 20h4"}],["path",{d:"M8 20V8a4 4 0 0 1 7.464-2"}]],Rn=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m4.93 4.93 4.24 4.24"}],["path",{d:"m14.83 9.17 4.24-4.24"}],["path",{d:"m14.83 14.83 4.24 4.24"}],["path",{d:"m9.17 14.83-4.24 4.24"}],["circle",{cx:"12",cy:"12",r:"4"}]],bn=[["path",{d:"M16.8 11.2c.8-.9 1.2-2 1.2-3.2a6 6 0 0 0-9.3-5"}],["path",{d:"m2 2 20 20"}],["path",{d:"M6.3 6.3a4.67 4.67 0 0 0 1.2 5.2c.7.7 1.3 1.5 1.5 2.5"}],["path",{d:"M9 18h6"}],["path",{d:"M10 22h4"}]],Tn=[["path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"}],["path",{d:"M9 18h6"}],["path",{d:"M10 22h4"}]],qn=[["path",{d:"M12 3V2"}],["path",{d:"M16.066 16.865 7 22l2-11V6a3 3 0 016 0v5l2 11"}],["path",{d:"m19.792 4.5.866-.5"}],["path",{d:"m19.797 13.5.866.5"}],["path",{d:"M21 9h1"}],["path",{d:"M3 9H2"}],["path",{d:"m4.203 13.5-.866.5"}],["path",{d:"M4.208 4.5 3.342 4"}],["path",{d:"M5.5 22h13"}],["path",{d:"m7.932 16.875 7.377-4.178"}],["path",{d:"M8 11h8"}],["path",{d:"M8 7h8"}]],Un=[["path",{d:"M12 3v12"}],["circle",{cx:"12",cy:"18",r:"3"}]],On=[["path",{d:"M9 12h12"}],["circle",{cx:"6",cy:"12",r:"3"}]],Gn=[["path",{d:"M 3 12 L 15 12"}],["circle",{cx:"18",cy:"12",r:"3"}]],Zn=[["path",{d:"M12 9v12"}],["circle",{cx:"12",cy:"6",r:"3"}]],En=[["path",{d:"M15 14H9"}],["path",{d:"M21 21H3"}],["path",{d:"M21 3H3"}],["path",{d:"m8 16 3.453-7.648a.6.6 0 011.094.001L16 16"}]],Wn=[["path",{d:"M7 3.5c5-2 7 2.5 3 4C1.5 10 2 15 5 16c5 2 9-10 14-7s.5 13.5-4 12c-5-2.5.5-11 6-2"}]],In=[["path",{d:"M11 5h2"}],["path",{d:"M15 12h6"}],["path",{d:"M19 5h2"}],["path",{d:"M3 12h6"}],["path",{d:"M3 19h18"}],["path",{d:"M3 5h2"}]],Xn=[["path",{d:"M9 17H7A5 5 0 0 1 7 7"}],["path",{d:"M15 7h2a5 5 0 0 1 4 8"}],["line",{x1:"8",x2:"12",y1:"12",y2:"12"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],Nn=[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12"}]],Kn=[["path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"}],["path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"}]],Jn=[["path",{d:"M16 5H3"}],["path",{d:"M16 12H3"}],["path",{d:"M11 19H3"}],["path",{d:"m15 18 2 2 4-4"}]],Qn=[["path",{d:"M13 5h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 19h8"}],["path",{d:"m3 17 2 2 4-4"}],["path",{d:"m3 7 2 2 4-4"}]],jn=[["path",{d:"M3 5h8"}],["path",{d:"M3 12h8"}],["path",{d:"M3 19h8"}],["path",{d:"m15 5 3 3 3-3"}],["path",{d:"m15 19 3-3 3 3"}]],Yn=[["path",{d:"M3 5h8"}],["path",{d:"M3 12h8"}],["path",{d:"M3 19h8"}],["path",{d:"m15 8 3-3 3 3"}],["path",{d:"m15 16 3 3 3-3"}]],_n=[["path",{d:"M16 13v2.2l1.6 1"}],["path",{d:"M3 12h3.458"}],["path",{d:"M3 19h3.832"}],["path",{d:"M3 5h18"}],["circle",{cx:"16",cy:"15",r:"6"}]],xn=[["path",{d:"M10 5h11"}],["path",{d:"M10 12h11"}],["path",{d:"M10 19h11"}],["path",{d:"m3 10 3-3-3-3"}],["path",{d:"m3 20 3-3-3-3"}]],ae=[["path",{d:"M16 5H3"}],["path",{d:"M16 12H3"}],["path",{d:"M9 19H3"}],["path",{d:"m16 16-3 3 3 3"}],["path",{d:"M21 5v12a2 2 0 0 1-2 2h-6"}]],he=[["path",{d:"M12 5H2"}],["path",{d:"M6 12h12"}],["path",{d:"M9 19h6"}],["path",{d:"M16 5h6"}],["path",{d:"M19 8V2"}]],te=[["path",{d:"M2 5h20"}],["path",{d:"M6 12h12"}],["path",{d:"M9 19h6"}]],v=[["path",{d:"M21 5H11"}],["path",{d:"M21 12H11"}],["path",{d:"M21 19H11"}],["path",{d:"m7 8-4 4 4 4"}]],$=[["path",{d:"M21 5H11"}],["path",{d:"M21 12H11"}],["path",{d:"M21 19H11"}],["path",{d:"m3 8 4 4-4 4"}]],de=[["path",{d:"M16 5H3"}],["path",{d:"M11 12H3"}],["path",{d:"M16 19H3"}],["path",{d:"M21 12h-6"}]],ce=[["path",{d:"M16 5H3"}],["path",{d:"M11 12H3"}],["path",{d:"M11 19H3"}],["path",{d:"M21 16V5"}],["circle",{cx:"18",cy:"16",r:"3"}]],Me=[["path",{d:"M11 5h10"}],["path",{d:"M11 12h10"}],["path",{d:"M11 19h10"}],["path",{d:"M4 4h1v5"}],["path",{d:"M4 9h2"}],["path",{d:"M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02"}]],pe=[["path",{d:"M16 5H3"}],["path",{d:"M11 12H3"}],["path",{d:"M16 19H3"}],["path",{d:"M18 9v6"}],["path",{d:"M21 12h-6"}]],le=[["path",{d:"M21 5H3"}],["path",{d:"M7 12H3"}],["path",{d:"M7 19H3"}],["path",{d:"M12 18a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L11 14"}],["path",{d:"M11 10v4h4"}]],ie=[["path",{d:"M3 19h18"}],["path",{d:"M15 12H3"}],["path",{d:"M9 5H3"}]],ne=[["path",{d:"M13 5h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 19h8"}],["path",{d:"m3 17 2 2 4-4"}],["rect",{x:"3",y:"4",width:"6",height:"6",rx:"1"}]],ee=[["path",{d:"M15 12H3"}],["path",{d:"M3 5h18"}],["path",{d:"M9 19H3"}]],re=[["path",{d:"M3 5h6"}],["path",{d:"M3 12h13"}],["path",{d:"M3 19h13"}],["path",{d:"m16 8-3-3 3-3"}],["path",{d:"M21 19V7a2 2 0 0 0-2-2h-6"}]],oe=[["path",{d:"M8 5h13"}],["path",{d:"M13 12h8"}],["path",{d:"M13 19h8"}],["path",{d:"M3 10a2 2 0 0 0 2 2h3"}],["path",{d:"M3 5v12a2 2 0 0 0 2 2h3"}]],ve=[["path",{d:"M21 5H3"}],["path",{d:"M10 12H3"}],["path",{d:"M10 19H3"}],["path",{d:"M15 12.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997a1 1 0 0 1-1.517-.86z"}]],$e=[["path",{d:"M16 5H3"}],["path",{d:"M11 12H3"}],["path",{d:"M16 19H3"}],["path",{d:"m15.5 9.5 5 5"}],["path",{d:"m20.5 9.5-5 5"}]],me=[["path",{d:"M3 5h.01"}],["path",{d:"M3 12h.01"}],["path",{d:"M3 19h.01"}],["path",{d:"M8 5h13"}],["path",{d:"M8 12h13"}],["path",{d:"M8 19h13"}]],T2=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]],ye=[["path",{d:"M22 12a1 1 0 0 1-10 0 1 1 0 0 0-10 0"}],["path",{d:"M7 20.7a1 1 0 1 1 5-8.7 1 1 0 1 0 5-8.6"}],["path",{d:"M7 3.3a1 1 0 1 1 5 8.6 1 1 0 1 0 5 8.6"}],["circle",{cx:"12",cy:"12",r:"10"}]],se=[["path",{d:"M12 2v4"}],["path",{d:"m16.2 7.8 2.9-2.9"}],["path",{d:"M18 12h4"}],["path",{d:"m16.2 16.2 2.9 2.9"}],["path",{d:"M12 18v4"}],["path",{d:"m4.9 19.1 2.9-2.9"}],["path",{d:"M2 12h4"}],["path",{d:"m4.9 4.9 2.9 2.9"}]],Ce=[["line",{x1:"2",x2:"5",y1:"12",y2:"12"}],["line",{x1:"19",x2:"22",y1:"12",y2:"12"}],["line",{x1:"12",x2:"12",y1:"2",y2:"5"}],["line",{x1:"12",x2:"12",y1:"19",y2:"22"}],["circle",{cx:"12",cy:"12",r:"7"}],["circle",{cx:"12",cy:"12",r:"3"}]],ue=[["line",{x1:"2",x2:"5",y1:"12",y2:"12"}],["line",{x1:"19",x2:"22",y1:"12",y2:"12"}],["line",{x1:"12",x2:"12",y1:"2",y2:"5"}],["line",{x1:"12",x2:"12",y1:"19",y2:"22"}],["circle",{cx:"12",cy:"12",r:"7"}]],ge=[["path",{d:"M12 19v3"}],["path",{d:"M12 2v3"}],["path",{d:"M18.89 13.24a7 7 0 0 0-8.13-8.13"}],["path",{d:"M19 12h3"}],["path",{d:"M2 12h3"}],["path",{d:"m2 2 20 20"}],["path",{d:"M7.05 7.05a7 7 0 0 0 9.9 9.9"}]],q2=[["circle",{cx:"12",cy:"16",r:"1"}],["rect",{width:"18",height:"12",x:"3",y:"10",rx:"2"}],["path",{d:"M7 10V7a5 5 0 0 1 9.33-2.5"}]],Ae=[["circle",{cx:"12",cy:"16",r:"1"}],["rect",{x:"3",y:"10",width:"18",height:"12",rx:"2"}],["path",{d:"M7 10V7a5 5 0 0 1 10 0v3"}]],U2=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2"}],["path",{d:"M7 11V7a5 5 0 0 1 9.9-1"}]],He=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4"}]],Ve=[["path",{d:"m10 17 5-5-5-5"}],["path",{d:"M15 12H3"}],["path",{d:"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"}]],Se=[["path",{d:"M3 5h1"}],["path",{d:"M3 12h1"}],["path",{d:"M3 19h1"}],["path",{d:"M8 5h1"}],["path",{d:"M8 12h1"}],["path",{d:"M8 19h1"}],["path",{d:"M13 5h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 19h8"}]],we=[["path",{d:"m16 17 5-5-5-5"}],["path",{d:"M21 12H9"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"}]],Le=[["circle",{cx:"11",cy:"11",r:"8"}],["path",{d:"m21 21-4.3-4.3"}],["path",{d:"M11 11a2 2 0 0 0 4 0 4 4 0 0 0-8 0 6 6 0 0 0 12 0"}]],fe=[["path",{d:"M6 20a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2"}],["path",{d:"M8 18V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v14"}],["path",{d:"M10 20h4"}],["circle",{cx:"16",cy:"20",r:"2"}],["circle",{cx:"8",cy:"20",r:"2"}]],ke=[["path",{d:"m12 15 4 4"}],["path",{d:"M2.352 10.648a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.029-6.029a1 1 0 1 1 3 3l-6.029 6.029a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.365-6.367A1 1 0 0 0 8.716 4.282z"}],["path",{d:"m5 8 4 4"}]],Pe=[["path",{d:"M22 7.7V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8.25"}],["path",{d:"M12 12.996a1.94 1.94 0 0 1-1.03-.296L2 7"}],["path",{d:"m20.69 16.479 1.29 4.88a.5.5 0 0 1-.698.591l-1.843-.849a1 1 0 0 0-.879.001l-1.846.85a.5.5 0 0 1-.692-.593l1.29-4.88"}],["circle",{cx:"19",cy:"14",r:"3"}]],Be=[["path",{d:"M22 13V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"m16 19 2 2 4-4"}]],ze=[["path",{d:"M16 14v2.2l1.6 1"}],["path",{d:"m22 7-.759.484"}],["path",{d:"M6.835 20H4a2 2 0 01-2-2V6a2 2 0 012-2h16a2 2 0 012 2v2"}],["path",{d:"M7.605 10.567 2 7"}],["circle",{cx:"16",cy:"16",r:"6"}]],De=[["path",{d:"M12.674 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4.675"}],["path",{d:"m2 7 9 5.701a2 2 0 0 0 2 0l1.084-.686"}],["circle",{cx:"19",cy:"7",r:"3"}]],Fe=[["path",{d:"M22 15V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"M16 19h6"}]],Re=[["path",{d:"M21.2 8.4c.5.38.8.97.8 1.6v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V10a2 2 0 0 1 .8-1.6l8-6a2 2 0 0 1 2.4 0l8 6Z"}],["path",{d:"m22 10-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 10"}]],be=[["path",{d:"M15.363 17.634a2 2 0 00-.506.854l-.837 2.87a.5.5 0 00.62.62l2.87-.837a2 2 0 00.854-.506l3.013-3.009a1 1 0 10-3.004-3.004z"}],["path",{d:"M22 10.38V6a2 2 0 00-2-2H4a2 2 0 00-2 2v12a2 2 0 002 2h6.25"}],["path",{d:"m22 7-8.991 5.727a2 2 0 01-2.009 0L2 7"}]],Te=[["path",{d:"M22 13V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"M19 16v6"}],["path",{d:"M16 19h6"}]],O2=[["path",{d:"M22 10.5V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h12.5"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"M18 15.28c.2-.4.5-.8.9-1a2.1 2.1 0 0 1 2.6.4c.3.4.5.8.5 1.3 0 1.3-2 2-2 2"}],["path",{d:"M20 22v.01"}]],qe=[["path",{d:"M22 12.5V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h7.5"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"M18 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"}],["circle",{cx:"18",cy:"18",r:"3"}],["path",{d:"m22 22-1.5-1.5"}]],Ue=[["path",{d:"M22 10.5V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h12.5"}],["path",{d:"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}],["path",{d:"M20 14v4"}],["path",{d:"M20 22v.01"}]],Oe=[["path",{d:"M22 12.532V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8.792"}],["path",{d:"m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"}],["path",{d:"m16.5 16.5 5 5"}],["path",{d:"m21.5 16.5-5 5"}]],Ge=[["path",{d:"m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"}],["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}]],Ze=[["path",{d:"M17 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 1-1.732"}],["path",{d:"m22 5.5-6.419 4.179a2 2 0 0 1-2.162 0L7 5.5"}],["rect",{x:"7",y:"3",width:"15",height:"12",rx:"2"}]],Ee=[["path",{d:"m11 19-1.106-.552a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0l4.212 2.106a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619V14"}],["path",{d:"M15 5.764V14"}],["path",{d:"M21 18h-6"}],["path",{d:"M9 3.236v15"}]],We=[["path",{d:"M19.43 12.935c.357-.967.57-1.955.57-2.935a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 1.202 0 32.197 32.197 0 0 0 .813-.728"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"m16 18 2 2 4-4"}]],Ie=[["path",{d:"M22 17a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9.5C2 7 4 5 6.5 5H18c2.2 0 4 1.8 4 4v8Z"}],["polyline",{points:"15,9 18,9 18,11"}],["path",{d:"M6.5 5C9 5 11 7 11 9.5V17a2 2 0 0 1-2 2"}],["line",{x1:"6",x2:"7",y1:"10",y2:"10"}]],Xe=[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"}],["path",{d:"m9 10 2 2 4-4"}]],Ne=[["path",{d:"M15 22a1 1 0 0 1-1-1v-4a1 1 0 0 1 .445-.832l3-2a1 1 0 0 1 1.11 0l3 2A1 1 0 0 1 22 17v4a1 1 0 0 1-1 1z"}],["path",{d:"M18 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 .601.2"}],["path",{d:"M18 22v-3"}],["circle",{cx:"10",cy:"10",r:"3"}]],Ke=[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"}],["path",{d:"M9 10h6"}]],Je=[["path",{d:"M18.977 14C19.6 12.701 20 11.343 20 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 1.202 0 32 32 0 0 0 .824-.738"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"M16 18h6"}]],Qe=[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"}],["path",{d:"M12 7v6"}],["path",{d:"M9 10h6"}]],G2=[["path",{d:"M17.97 9.304A8 8 0 0 0 2 10c0 4.69 4.887 9.562 7.022 11.468"}],["path",{d:"M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}],["circle",{cx:"10",cy:"10",r:"3"}]],je=[["path",{d:"M12.75 7.09a3 3 0 0 1 2.16 2.16"}],["path",{d:"M17.072 17.072c-1.634 2.17-3.527 3.912-4.471 4.727a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 1.432-4.568"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.475 2.818A8 8 0 0 1 20 10c0 1.183-.31 2.377-.81 3.533"}],["path",{d:"M9.13 9.13a3 3 0 0 0 3.74 3.74"}]],Ye=[["path",{d:"M 12.248 21.969 a 1 1 0 0 1 -0.849 -0.17 C 9.539 20.193 4 14.993 4 10 a 8 8 0 0 1 16 0 C 20 10.42 19.961 10.841 19.888 11.262"}],["path",{d:"m22 22-1.88-1.88"}],["circle",{cx:"12",cy:"10",r:"3"}],["circle",{cx:"18",cy:"18",r:"3"}]],_e=[["path",{d:"M19.914 11.105A7.298 7.298 0 0 0 20 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 1.202 0 32 32 0 0 0 .824-.738"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"M16 18h6"}],["path",{d:"M19 15v6"}]],xe=[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"}],["path",{d:"m14.5 7.5-5 5"}],["path",{d:"m9.5 7.5 5 5"}]],ar=[["path",{d:"M19.752 11.901A7.78 7.78 0 0 0 20 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 1.202 0 19 19 0 0 0 .09-.077"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"m21.5 15.5-5 5"}],["path",{d:"m21.5 20.5-5-5"}]],hr=[["path",{d:"M18 8c0 3.613-3.869 7.429-5.393 8.795a1 1 0 01-1.214 0C9.87 15.429 6 11.613 6 8a6 6 0 0112 0"}],["path",{d:"M4.474 15h-.197a1 1 0 00-.969.753l-1.097 4.35a1.5 1.5 0 001.444 1.898L20.344 22a1.5 1.5 0 001.446-1.897l-1.098-4.35a1 1 0 00-.969-.753h-.197"}],["circle",{cx:"12",cy:"8",r:"2"}]],tr=[["path",{d:"m11 19-1.106-.552a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0l4.212 2.106a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619V12"}],["path",{d:"M15 5.764V12"}],["path",{d:"M18 15v6"}],["path",{d:"M21 18h-6"}],["path",{d:"M9 3.236v15"}]],dr=[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"}],["circle",{cx:"12",cy:"10",r:"3"}]],cr=[["path",{d:"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"}],["path",{d:"M15 5.764v15"}],["path",{d:"M9 3.236v15"}]],Mr=[["path",{d:"m14 6 4 4"}],["path",{d:"M17 3h4v4"}],["path",{d:"m21 3-7.75 7.75"}],["circle",{cx:"9",cy:"15",r:"6"}]],pr=[["path",{d:"M16 3h5v5"}],["path",{d:"m21 3-6.75 6.75"}],["circle",{cx:"10",cy:"14",r:"6"}]],lr=[["path",{d:"M15 3h6v6"}],["path",{d:"m21 3-7 7"}],["path",{d:"m3 21 7-7"}],["path",{d:"M9 21H3v-6"}]],ir=[["path",{d:"M8 3H5a2 2 0 0 0-2 2v3"}],["path",{d:"M21 8V5a2 2 0 0 0-2-2h-3"}],["path",{d:"M3 16v3a2 2 0 0 0 2 2h3"}],["path",{d:"M16 21h3a2 2 0 0 0 2-2v-3"}]],nr=[["path",{d:"M12 12 4.207 4.207A.707.707 0 0 1 4.707 3h14.586a.707.707 0 0 1 .5 1.207z"}],["path",{d:"M12 12v10"}],["path",{d:"M7 22h10"}]],er=[["path",{d:"M7.21 15 2.66 7.14a2 2 0 0 1 .13-2.2L4.4 2.8A2 2 0 0 1 6 2h12a2 2 0 0 1 1.6.8l1.6 2.14a2 2 0 0 1 .14 2.2L16.79 15"}],["path",{d:"M11 12 5.12 2.2"}],["path",{d:"m13 12 5.88-9.8"}],["path",{d:"M8 7h8"}],["circle",{cx:"12",cy:"17",r:"5"}],["path",{d:"M12 18v-2h-.5"}]],rr=[["path",{d:"M11.636 6A13 13 0 0 0 19.4 3.2 1 1 0 0 1 21 4v11.344"}],["path",{d:"M14.378 14.357A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h1"}],["path",{d:"m2 2 20 20"}],["path",{d:"M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"}],["path",{d:"M8 8v6"}]],or=[["path",{d:"M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"}],["path",{d:"M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"}],["path",{d:"M8 6v8"}]],vr=[["path",{d:"M12 12v-2"}],["path",{d:"M12 18v-2"}],["path",{d:"M16 12v-2"}],["path",{d:"M16 18v-2"}],["path",{d:"M2 11h1.5"}],["path",{d:"M20 18v-2"}],["path",{d:"M20.5 11H22"}],["path",{d:"M4 18v-2"}],["path",{d:"M8 12v-2"}],["path",{d:"M8 18v-2"}],["rect",{x:"2",y:"6",width:"20",height:"10",rx:"2"}]],$r=[["path",{d:"M4 5h16"}],["path",{d:"M4 12h16"}],["path",{d:"M4 19h16"}]],mr=[["path",{d:"m8 6 4-4 4 4"}],["path",{d:"M12 2v10.3a4 4 0 0 1-1.172 2.872L4 22"}],["path",{d:"m20 22-5-5"}]],yr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"m16 9-5.5 5.5L8 12"}]],sr=[["path",{d:"m10 9-3 3 3 3"}],["path",{d:"m14 15 3-3-3-3"}],["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}]],Cr=[["path",{d:"M10.1 2.182a10 10 0 013.8 0"}],["path",{d:"M13.9 21.818a10 10 0 01-3.8 0"}],["path",{d:"M17.609 3.72a10 10 0 012.69 2.7"}],["path",{d:"M2.182 13.9a10 10 0 010-3.8"}],["path",{d:"M20.28 17.61a10 10 0 01-2.7 2.69"}],["path",{d:"M21.818 10.1a10 10 0 010 3.8"}],["path",{d:"M3.721 6.391a10 10 0 012.7-2.69"}],["path",{d:"m6.163 21.117-2.906.85a1 1 0 01-1.236-1.169l.965-2.98"}],["path",{d:"m16 9-5.5 5.5L8 12"}]],ur=[["path",{d:"M10.1 2.182a10 10 0 0 1 3.8 0"}],["path",{d:"M13.9 21.818a10 10 0 0 1-3.8 0"}],["path",{d:"M17.609 3.72a10 10 0 0 1 2.69 2.7"}],["path",{d:"M2.182 13.9a10 10 0 0 1 0-3.8"}],["path",{d:"M20.28 17.61a10 10 0 0 1-2.7 2.69"}],["path",{d:"M21.818 10.1a10 10 0 0 1 0 3.8"}],["path",{d:"M3.721 6.391a10 10 0 0 1 2.7-2.69"}],["path",{d:"m6.163 21.117-2.906.85a1 1 0 0 1-1.236-1.169l.965-2.98"}]],gr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"M7.828 13.07A3 3 0 0 1 12 8.764a3 3 0 0 1 5.004 2.224 3 3 0 0 1-.832 2.083l-3.447 3.62a1 1 0 0 1-1.45-.001z"}]],Ar=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"M8 12h.01"}],["path",{d:"M12 12h.01"}],["path",{d:"M16 12h.01"}]],Hr=[["path",{d:"m2 2 20 20"}],["path",{d:"M4.93 4.929a10 10 0 0 0-1.938 11.412 2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 0 0 11.302-1.989"}],["path",{d:"M8.35 2.69A10 10 0 0 1 21.3 15.65"}]],Vr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"M8 12h8"}],["path",{d:"M12 8v8"}]],Sr=[["path",{d:"M3.086 17.509a2 2 0 00-.094-1.167 10 10 0 114.777 4.719 2 2 0 00-1.099-.092l-3.413.998a1 1 0 01-1.236-1.168z"}],["path",{d:"M8 12h9"}],["path",{d:"M8 16h5"}],["path",{d:"M8 8h7"}]],wr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"m10 15-3-3 3-3"}],["path",{d:"M7 12h8a2 2 0 0 1 2 2v1"}]],Z2=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]],Lr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"M12 8v4"}],["path",{d:"M12 16h.01"}]],fr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]],kr=[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"}]],Pr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.7.7 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"m9 11 2 2 4-4"}]],Br=[["path",{d:"M14 3h2"}],["path",{d:"M16 19h-2"}],["path",{d:"M2 12v-2"}],["path",{d:"M2 16v5.286a.71.71 0 0 0 1.212.502l1.149-1.149"}],["path",{d:"M20 19a2 2 0 0 0 2-2v-1"}],["path",{d:"M22 10v2"}],["path",{d:"M22 6V5a2 2 0 0 0-2-2"}],["path",{d:"M4 3a2 2 0 0 0-2 2v1"}],["path",{d:"M8 19h2"}],["path",{d:"M8 3h2"}]],zr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"m10 8-3 3 3 3"}],["path",{d:"m14 14 3-3-3-3"}]],Dr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M10 15h4"}],["path",{d:"M10 9h4"}],["path",{d:"M12 7v4"}]],Fr=[["path",{d:"M12.7 3H4a2 2 0 0 0-2 2v16.286a.71.71 0 0 0 1.212.502l2.202-2.202A2 2 0 0 1 6.828 19H20a2 2 0 0 0 2-2v-4.7"}],["circle",{cx:"19",cy:"6",r:"3"}]],Rr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M7.5 9.5c0 .687.265 1.383.697 1.844l3.009 3.264a1.14 1.14 0 0 0 .407.314 1 1 0 0 0 .783-.004 1.14 1.14 0 0 0 .398-.31l3.008-3.264A2.77 2.77 0 0 0 16.5 9.5 2.5 2.5 0 0 0 12 8a2.5 2.5 0 0 0-4.5 1.5"}]],br=[["path",{d:"M22 8.5V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v16.286a.71.71 0 0 0 1.212.502l2.202-2.202A2 2 0 0 1 6.828 19H10"}],["path",{d:"M20 15v-2a2 2 0 0 0-4 0v2"}],["rect",{x:"14",y:"15",width:"8",height:"5",rx:"1"}]],Tr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M12 11h.01"}],["path",{d:"M16 11h.01"}],["path",{d:"M8 11h.01"}]],qr=[["path",{d:"M19 19H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.7.7 0 0 1 2 21.286V5a2 2 0 0 1 1.184-1.826"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8.656 3H20a2 2 0 0 1 2 2v11.344"}]],Ur=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M12 8v6"}],["path",{d:"M9 11h6"}]],Or=[["path",{d:"M12 3H4a2 2 0 0 0-2 2v16.286a.71.71 0 0 0 1.212.502l2.202-2.202A2 2 0 0 1 6.828 19H20a2 2 0 0 0 2-2v-4"}],["path",{d:"M16 3h6v6"}],["path",{d:"m16 9 6-6"}]],Gr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"m10 8-3 3 3 3"}],["path",{d:"M17 14v-1a2 2 0 0 0-2-2H7"}]],Zr=[["path",{d:"M14 14a2 2 0 0 0 2-2V8h-2"}],["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M8 14a2 2 0 0 0 2-2V8H8"}]],Er=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M7 11h10"}],["path",{d:"M7 15h6"}],["path",{d:"M7 7h8"}]],Wr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"M12 15h.01"}],["path",{d:"M12 7v4"}]],Ir=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}],["path",{d:"m14.5 8.5-5 5"}],["path",{d:"m9.5 8.5 5 5"}]],Xr=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"}]],Nr=[["path",{d:"M19.95 10.05a7 7 0 011.412 7.872 1 1 0 00-.058.787l.675 2.089a1 1 0 01-1.236 1.168l-2.155-.631a1 1 0 00-.745.06 7 7 0 01-7.793-1.445"}],["path",{d:"M2.696 12.708a1 1 0 00-.058-.785 7 7 0 113.518 3.473 1 1 0 00-.744-.061l-2.155.63a1 1 0 01-1.236-1.167z"}]],Kr=[["path",{d:"M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"}],["path",{d:"M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1"}]],Jr=[["path",{d:"M12 11.4V9.1"}],["path",{d:"m12 17 6.59-6.59"}],["path",{d:"m15.05 5.7-.218-.691a3 3 0 0 0-5.663 0L4.418 19.695A1 1 0 0 0 5.37 21h13.253a1 1 0 0 0 .951-1.31L18.45 16.2"}],["circle",{cx:"20",cy:"9",r:"2"}]],Qr=[["path",{d:"M10 3v2.341"}],["path",{d:"M12 17v4"}],["path",{d:"M14 5v.341"}],["path",{d:"M18 5v13"}],["path",{d:"M2 10v3"}],["path",{d:"M22 10v3"}],["path",{d:"M6 6v11"}],["path",{d:"M9 21h6"}],["rect",{width:"4",height:"8",x:"10",y:"9",rx:"2"}]],jr=[["path",{d:"M12 19v3"}],["path",{d:"M15 9.34V5a3 3 0 0 0-5.68-1.33"}],["path",{d:"M16.95 16.95A7 7 0 0 1 5 12v-2"}],["path",{d:"M18.89 13.23A7 7 0 0 0 19 12v-2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M9 9v3a3 3 0 0 0 5.12 2.12"}]],E2=[["path",{d:"M12 17v4"}],["path",{d:"M18 11a6 6 0 00-3-5.197"}],["path",{d:"M2 11a10 10 0 015-8.662"}],["path",{d:"M22 11a10 10 0 00-5-8.662"}],["path",{d:"M6 11a6 6 0 013-5.197"}],["path",{d:"M9 21h6"}],["rect",{x:"10",y:"9",width:"4",height:"8",rx:"2"}]],Yr=[["path",{d:"M12 19v3"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"}],["rect",{x:"9",y:"2",width:"6",height:"13",rx:"3"}]],W2=[["path",{d:"m11 7.601-5.994 8.19a1 1 0 0 0 .1 1.298l.817.818a1 1 0 0 0 1.314.087L15.09 12"}],["path",{d:"M16.5 21.174C15.5 20.5 14.372 20 13 20c-2.058 0-3.928 2.356-6 2-2.072-.356-2.775-3.369-1.5-4.5"}],["circle",{cx:"16",cy:"7",r:"5"}]],_r=[["path",{d:"M10 12h4"}],["path",{d:"M10 17h4"}],["path",{d:"M10 7h4"}],["path",{d:"M18 12h2"}],["path",{d:"M18 18h2"}],["path",{d:"M18 6h2"}],["path",{d:"M4 12h2"}],["path",{d:"M4 18h2"}],["path",{d:"M4 6h2"}],["rect",{x:"6",y:"2",width:"12",height:"20",rx:"2"}]],xr=[["path",{d:"M6 18h8"}],["path",{d:"M3 22h18"}],["path",{d:"M14 22a7 7 0 1 0 0-14h-1"}],["path",{d:"M9 14h2"}],["path",{d:"M9 12a2 2 0 0 1-2-2V6h6v4a2 2 0 0 1-2 2Z"}],["path",{d:"M12 6V3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3"}]],ao=[["rect",{width:"20",height:"15",x:"2",y:"4",rx:"2"}],["rect",{width:"8",height:"7",x:"6",y:"8",rx:"1"}],["path",{d:"M18 8v7"}],["path",{d:"M6 19v2"}],["path",{d:"M18 19v2"}]],ho=[["path",{d:"M12 18h.01"}],["path",{d:"M15 2.458V5a1 1 0 01-1 1h-4a1 1 0 01-1-1V2.458"}],["path",{d:"M16 16h.01"}],["path",{d:"M18 12h.01"}],["path",{d:"M6 12h.01"}],["path",{d:"M8 16h.01"}],["circle",{cx:"12",cy:"12",r:"10"}]],to=[["path",{d:"M12 13v8"}],["path",{d:"M12 3v3"}],["path",{d:"M18.172 6a2 2 0 0 1 1.414.586l2.06 2.06a1.207 1.207 0 0 1 0 1.708l-2.06 2.06a2 2 0 0 1-1.414.586H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"}]],co=[["path",{d:"M8 2h8"}],["path",{d:"M9 2v1.343M15 2v2.789a4 4 0 0 0 .672 2.219l.656.984a4 4 0 0 1 .672 2.22v1.131M7.8 7.8l-.128.192A4 4 0 0 0 7 10.212V20a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-3"}],["path",{d:"M7 15a6.47 6.47 0 0 1 5 0 6.472 6.472 0 0 0 3.435.435"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],Mo=[["path",{d:"m14 10 7-7"}],["path",{d:"M20 10h-6V4"}],["path",{d:"m3 21 7-7"}],["path",{d:"M4 14h6v6"}]],po=[["path",{d:"M8 2h8"}],["path",{d:"M9 2v2.789a4 4 0 0 1-.672 2.219l-.656.984A4 4 0 0 0 7 10.212V20a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-9.789a4 4 0 0 0-.672-2.219l-.656-.984A4 4 0 0 1 15 4.788V2"}],["path",{d:"M7 15a6.472 6.472 0 0 1 5 0 6.47 6.47 0 0 0 5 0"}]],lo=[["path",{d:"M8 3v3a2 2 0 0 1-2 2H3"}],["path",{d:"M21 8h-3a2 2 0 0 1-2-2V3"}],["path",{d:"M3 16h3a2 2 0 0 1 2 2v3"}],["path",{d:"M16 21v-3a2 2 0 0 1 2-2h3"}]],io=[["path",{d:"M5 12h14"}]],no=[["path",{d:"M11 6 8 9"}],["path",{d:"m16 7-8 8"}],["rect",{x:"4",y:"2",width:"16",height:"20",rx:"2"}]],eo=[["path",{d:"M10 6.6 8.6 8"}],["path",{d:"M12 18v4"}],["path",{d:"M15 7.5 9.5 13"}],["path",{d:"M7 22h10"}],["circle",{cx:"12",cy:"10",r:"8"}]],ro=[["path",{d:"M11 13a3 3 0 1 1 2.83-4H14a2 2 0 0 1 0 4z"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}]],oo=[["path",{d:"m9 10 2 2 4-4"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}]],vo=[["path",{d:"M12 17v4"}],["path",{d:"m14.305 7.53.923-.382"}],["path",{d:"m15.228 4.852-.923-.383"}],["path",{d:"m16.852 3.228-.383-.924"}],["path",{d:"m16.852 8.772-.383.923"}],["path",{d:"m19.148 3.228.383-.924"}],["path",{d:"m19.53 9.696-.382-.924"}],["path",{d:"m20.772 4.852.924-.383"}],["path",{d:"m20.772 7.148.924.383"}],["path",{d:"M22 13v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"}],["path",{d:"M8 21h8"}],["circle",{cx:"18",cy:"6",r:"3"}]],$o=[["path",{d:"M12 17v4"}],["path",{d:"M22 12.307V15a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8.693"}],["path",{d:"M8 21h8"}],["circle",{cx:"19",cy:"6",r:"3"}]],mo=[["path",{d:"M12 13V7"}],["path",{d:"m15 10-3 3-3-3"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}]],yo=[["path",{d:"M12 17v4"}],["path",{d:"M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8 21h8"}],["path",{d:"M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042"}]],so=[["path",{d:"M10 13V7"}],["path",{d:"M14 13V7"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}]],Co=[["path",{d:"M10 15H4a2 2 0 01-2-2V7a2 2 0 012-2h6"}],["path",{d:"M10 19H5"}],["path",{d:"M14 11h8"}],["path",{d:"M14 7h8"}],["path",{d:"M18 17h.01"}],["path",{d:"M9 19v-4"}],["rect",{x:"14",y:"3",width:"8",height:"18",rx:"1"}]],uo=[["path",{d:"M15.033 9.44a.647.647 0 0 1 0 1.12l-4.065 2.352a.645.645 0 0 1-.968-.56V7.648a.645.645 0 0 1 .967-.56z"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}]],go=[["path",{d:"M18 8V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h8"}],["path",{d:"M10 19v-3.96 3.15"}],["path",{d:"M7 19h5"}],["rect",{width:"6",height:"10",x:"16",y:"12",rx:"2"}]],Ao=[["path",{d:"M5.5 20H8"}],["path",{d:"M17 9h.01"}],["rect",{width:"10",height:"16",x:"12",y:"4",rx:"2"}],["path",{d:"M8 6H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h4"}],["circle",{cx:"17",cy:"15",r:"1"}]],Ho=[["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}],["rect",{x:"9",y:"7",width:"6",height:"6",rx:"1"}]],Vo=[["path",{d:"m9 10 3-3 3 3"}],["path",{d:"M12 13V7"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}]],So=[["path",{d:"m14.5 12.5-5-5"}],["path",{d:"m9.5 12.5 5-5"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}]],wo=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21"}]],Lo=[["path",{d:"M18 5h4"}],["path",{d:"M20 3v4"}],["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"}]],fo=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"}]],ko=[["path",{d:"M10 22a3 3 0 01-3-3"}],["path",{d:"M10 22c2.761 0 5-1.79 5-4-4.42 0-4.08-5-8.5-5a4.501 4.501 0 000 9z"}],["path",{d:"M10 3H8"}],["path",{d:"M12.5 11.5 22 2"}],["path",{d:"M20 13v4"}],["path",{d:"M22 15h-4"}],["path",{d:"M4 5v4"}],["path",{d:"M6 7H2"}],["path",{d:"m6.98 13.02 2.665-2.664a1.21 1.21 0 011.71 0l2.29 2.288a1.21 1.21 0 010 1.712l-2.088 2.087"}],["path",{d:"M9 2v2"}]],Po=[["path",{d:"M10 22c2.761 0 5-1.79 5-4-4.42 0-4.08-5-8.5-5a1 1 0 100 9za3 3 0 01-3-3"}],["path",{d:"M12.5 11.5 22 2"}],["path",{d:"m6.98 13.02 2.665-2.664a1.21 1.21 0 011.71 0l2.29 2.288a1.21 1.21 0 010 1.712l-2.088 2.087"}]],Bo=[["path",{d:"M12.268 2a2 2 0 003.465 2"}],["path",{d:"M14 5 L14 8"}],["path",{d:"M16 22v-3a2 2 0 00-4 0v3"}],["path",{d:"M21 13c-.662-1.497-1.666-2.753-2.9-3.63C16.825 8.47 15.422 8 14 8s-2.826.47-4.1 1.37C8.668 10.248 7.663 11.504 7 13z"}],["path",{d:"M3 9h4"}],["path",{d:"M7 22V6a5 5 0 00-2-4 5 5 0 00-2 4v14a2 2 0 002 2h14a2 2 0 002-2v-7"}]],zo=[["path",{d:"m18 14-1-3"}],["path",{d:"m3 9 6 2a2 2 0 0 1 2-2h2a2 2 0 0 1 1.99 1.81"}],["path",{d:"M8 17h3a1 1 0 0 0 1-1 6 6 0 0 1 6-6 1 1 0 0 0 1-1v-.75A5 5 0 0 0 17 5"}],["circle",{cx:"19",cy:"17",r:"3"}],["circle",{cx:"5",cy:"17",r:"3"}]],Do=[["path",{d:"m8 3 4 8 5-5 5 15H2L8 3z"}],["path",{d:"M4.14 15.08c2.62-1.57 5.24-1.43 7.86.42 2.74 1.94 5.49 2 8.23.19"}]],Fo=[["path",{d:"m8 3 4 8 5-5 5 15H2L8 3z"}]],Ro=[["path",{d:"M12 7.318V10"}],["path",{d:"M5 10v5a7 7 0 0 0 14 0V9c0-3.527-2.608-6.515-6-7"}],["circle",{cx:"7",cy:"4",r:"2"}]],bo=[["path",{d:"M12 6v.343"}],["path",{d:"M18.218 18.218A7 7 0 0 1 5 15V9a7 7 0 0 1 .782-3.218"}],["path",{d:"M19 13.343V9A7 7 0 0 0 8.56 2.902"}],["path",{d:"M22 22 2 2"}]],To=[["path",{d:"m15.55 8.45 5.138 2.087a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063L8.45 15.551"}],["path",{d:"M22 2 2 22"}],["path",{d:"m6.816 11.528-2.779-6.84a.495.495 0 0 1 .651-.651l6.84 2.779"}]],qo=[["path",{d:"M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z"}]],Uo=[["path",{d:"M2.034 2.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.944L8.204 7.545a1 1 0 0 0-.66.66l-1.066 3.443a.5.5 0 0 1-.944.033z"}],["circle",{cx:"16",cy:"16",r:"6"}],["path",{d:"m11.8 11.8 8.4 8.4"}]],Oo=[["path",{d:"M14 4.1 12 6"}],["path",{d:"m5.1 8-2.9-.8"}],["path",{d:"m6 12-1.9 2"}],["path",{d:"M7.2 2.2 8 5.1"}],["path",{d:"M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z"}]],Go=[["path",{d:"M12.586 12.586 19 19"}],["path",{d:"M3.688 3.037a.497.497 0 0 0-.651.651l6.5 15.999a.501.501 0 0 0 .947-.062l1.569-6.083a2 2 0 0 1 1.448-1.479l6.124-1.579a.5.5 0 0 0 .063-.947z"}]],Zo=[["path",{d:"M12 7.318V10"}],["path",{d:"M19 10v5a7 7 0 0 1-14 0V9c0-3.527 2.608-6.515 6-7"}],["circle",{cx:"17",cy:"4",r:"2"}]],Eo=[["rect",{x:"5",y:"2",width:"14",height:"20",rx:"7"}],["path",{d:"M12 6v4"}]],Wo=[["path",{d:"M13.074 7.417a2.6 2.6 0 012.989.099c1.829 1.36 3.053 2.076 5.479 3.644a1 1 0 01.308 1.368 11.6 11.6 0 01-1.617 2.05"}],["path",{d:"M2 12a50.5 50.5 0 0010.99.99"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 11a1 1 0 011 1 51 51 0 01-3.734.61"}],["path",{d:"M7.695 7.695c-1.7 1.247-2.92 1.967-5.238 3.464a1 1 0 00-.307 1.369 11.6 11.6 0 0014.766 4.388"}]],Io=[["path",{d:"M2 12a50.5 50.5 0 0020 0 1 1 0 00-1-1"}],["path",{d:"M2.457 11.159a1 1 0 00-.307 1.369 11.59 11.59 0 0019.7 0 1 1 0 00-.308-1.368c-2.426-1.568-3.65-2.284-5.479-3.644a2.6 2.6 0 00-3.373.208 1 1 0 01-1.38 0 2.62 2.62 0 00-3.373-.208c-1.83 1.36-3.053 2.076-5.48 3.643"}]],Xo=[["path",{d:"M19 13v6h-6"}],["path",{d:"M5 11V5h6"}],["path",{d:"m5 5 14 14"}]],I2=[["path",{d:"M5 3v16h16"}],["path",{d:"m5 19 6-6"}],["path",{d:"m2 6 3-3 3 3"}],["path",{d:"m18 16 3 3-3 3"}]],No=[["path",{d:"M11 19H5v-6"}],["path",{d:"M13 5h6v6"}],["path",{d:"M19 5 5 19"}]],Ko=[["path",{d:"M11 19H5V13"}],["path",{d:"M19 5L5 19"}]],Jo=[["path",{d:"M19 13V19H13"}],["path",{d:"M5 5L19 19"}]],Qo=[["path",{d:"M8 18L12 22L16 18"}],["path",{d:"M12 2V22"}]],jo=[["path",{d:"m18 8 4 4-4 4"}],["path",{d:"M2 12h20"}],["path",{d:"m6 8-4 4 4 4"}]],Yo=[["path",{d:"M6 8L2 12L6 16"}],["path",{d:"M2 12H22"}]],_o=[["path",{d:"M18 8L22 12L18 16"}],["path",{d:"M2 12H22"}]],xo=[["path",{d:"M5 11V5H11"}],["path",{d:"M5 5L19 19"}]],av=[["path",{d:"M13 5H19V11"}],["path",{d:"M19 5L5 19"}]],hv=[["path",{d:"M8 6L12 2L16 6"}],["path",{d:"M12 2V22"}]],tv=[["path",{d:"M12 2v20"}],["path",{d:"m15 19-3 3-3-3"}],["path",{d:"m19 9 3 3-3 3"}],["path",{d:"M2 12h20"}],["path",{d:"m5 9-3 3 3 3"}],["path",{d:"m9 5 3-3 3 3"}]],dv=[["path",{d:"M12 2v20"}],["path",{d:"m8 18 4 4 4-4"}],["path",{d:"m8 6 4-4 4 4"}]],cv=[["path",{d:"M9.31 9.31 5 21l7-4 7 4-1.17-3.17"}],["path",{d:"M14.53 8.88 12 2l-1.17 3.17"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],Mv=[["polygon",{points:"12 2 19 21 12 17 5 21 12 2"}]],pv=[["path",{d:"M8.43 8.43 3 11l8 2 2 8 2.57-5.43"}],["path",{d:"M17.39 11.73 22 2l-9.73 4.61"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],lv=[["polygon",{points:"3 11 22 2 13 21 11 13 3 11"}]],iv=[["path",{d:"M18 16.173 A4.74 4.74 0 0 0 13.496 8.005"}],["path",{d:"M4 3 L20 3"}],["path",{d:"M5 13 L13.5 21"}],["path",{d:"M5 13 L9 13"}],["path",{d:"M8 13 C15.5 13 14.667 3 8 3"}]],nv=[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3"}],["path",{d:"M12 12V8"}]],ev=[["path",{d:"M15 18h-5"}],["path",{d:"M18 14h-8"}],["path",{d:"M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2"}],["rect",{width:"8",height:"4",x:"10",y:"6",rx:"1"}]],rv=[["path",{d:"M6 8.32a7.43 7.43 0 0 1 0 7.36"}],["path",{d:"M9.46 6.21a11.76 11.76 0 0 1 0 11.58"}],["path",{d:"M12.91 4.1a15.91 15.91 0 0 1 .01 15.8"}],["path",{d:"M16.37 2a20.16 20.16 0 0 1 0 20"}]],ov=[["path",{d:"M12 2v10"}],["path",{d:"m8.5 4 7 4"}],["path",{d:"m8.5 8 7-4"}],["circle",{cx:"12",cy:"17",r:"5"}]],vv=[["path",{d:"M16 11.75V22"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["path",{d:"M2 6h4"}],["path",{d:"M20 11.75V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h5.65"}],["circle",{cx:"18",cy:"5",r:"3"}]],$v=[["path",{d:"M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4"}],["path",{d:"M2 6h4"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["path",{d:"M21.378 5.626a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}]],mv=[["path",{d:"M2 6h4"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2"}],["path",{d:"M15 2v20"}],["path",{d:"M15 7h5"}],["path",{d:"M15 12h5"}],["path",{d:"M15 17h5"}]],yv=[["path",{d:"M2 6h4"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2"}],["path",{d:"M9.5 8h5"}],["path",{d:"M9.5 12H16"}],["path",{d:"M9.5 16H14"}]],sv=[["path",{d:"M2 6h4"}],["path",{d:"M2 10h4"}],["path",{d:"M2 14h4"}],["path",{d:"M2 18h4"}],["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2"}],["path",{d:"M16 2v20"}]],Cv=[["path",{d:"M8 2v4"}],["path",{d:"M12 2v4"}],["path",{d:"M16 2v4"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v2"}],["path",{d:"M20 12v2"}],["path",{d:"M20 18v2a2 2 0 0 1-2 2h-1"}],["path",{d:"M13 22h-2"}],["path",{d:"M7 22H6a2 2 0 0 1-2-2v-2"}],["path",{d:"M4 14v-2"}],["path",{d:"M4 8V6a2 2 0 0 1 2-2h2"}],["path",{d:"M8 10h6"}],["path",{d:"M8 14h8"}],["path",{d:"M8 18h5"}]],uv=[["path",{d:"M8 2v4"}],["path",{d:"M12 2v4"}],["path",{d:"M16 2v4"}],["rect",{width:"16",height:"18",x:"4",y:"4",rx:"2"}],["path",{d:"M8 10h6"}],["path",{d:"M8 14h8"}],["path",{d:"M8 18h5"}]],gv=[["path",{d:"M11.868 11.868a.88.88 0 01-.488.252c-1.78.28-3.54-.17-4.88-.62 0 1.272-.229 3.578-.653 5.347a10 10 0 01-.417 1.363c-.21.52-.82.55-1.17.12a10 10 0 01.677-13.393"}],["path",{d:"M12.14 6.485a27.4 27.4 0 004.707-.638L20 9a7.23 7.23 0 011.706 7.05"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20.707 20.707A1 1 0 0120 21h-1c-1.069 0-1.648.242-2.485.552A7.2 7.2 0 019.002 20l-3.155-3.153"}],["path",{d:"M8.356 2.7a10 10 0 019.974 1.56c.43.35.4.97-.12 1.17a10 10 0 01-1.363.417"}]],Av=[["path",{d:"M16.847 5.847 20 9a7.23 7.23 0 011.551 7.516C21.241 17.352 21 17.932 21 19v1a1 1 0 01-1 1h-1c-1.069 0-1.648.242-2.485.552A7.2 7.2 0 019.002 20l-3.155-3.153"}],["path",{d:"M18.21 5.43c-1.71.69-5.07 1.07-6.71 1.07.46 1.38.91 2.74.61 4.88a.88.88 0 01-.73.74c-1.78.28-3.54-.17-4.88-.62 0 1.64-.38 5-1.07 6.71-.21.52-.82.55-1.17.12A10 10 0 0118.33 4.26c.43.35.4.97-.12 1.17"}],["path",{d:"M4.93 4.93 3 3a.7.7 0 010-1"}]],X2=[["path",{d:"M12 16h.01"}],["path",{d:"M12 8v4"}],["path",{d:"M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z"}]],Hv=[["path",{d:"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"}],["path",{d:"M8 12h8"}]],N2=[["path",{d:"M10 15V9"}],["path",{d:"M14 15V9"}],["path",{d:"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"}]],K2=[["path",{d:"m15 9-6 6"}],["path",{d:"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"}],["path",{d:"m9 9 6 6"}]],Vv=[["path",{d:"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"}]],Sv=[["path",{d:"M3 20h4.5a.5.5 0 0 0 .5-.5v-.282a.52.52 0 0 0-.247-.437 8 8 0 1 1 8.494-.001.52.52 0 0 0-.247.438v.282a.5.5 0 0 0 .5.5H21"}]],wv=[["path",{d:"M14 3h7"}],["path",{d:"M3 3h5.28a1 1 0 0 1 .948.684l5.544 16.632a1 1 0 0 0 .949.684H21"}]],Lv=[["path",{d:"M20.341 6.484A10 10 0 0 1 10.266 21.85"}],["path",{d:"M3.659 17.516A10 10 0 0 1 13.74 2.152"}],["circle",{cx:"12",cy:"12",r:"3"}],["circle",{cx:"19",cy:"5",r:"2"}],["circle",{cx:"5",cy:"19",r:"2"}]],fv=[["path",{d:"M12 12V4a1 1 0 0 1 1-1h6.297a1 1 0 0 1 .651 1.759l-4.696 4.025"}],["path",{d:"m12 21-7.414-7.414A2 2 0 0 1 4 12.172V6.415a1.002 1.002 0 0 1 1.707-.707L20 20.009"}],["path",{d:"m12.214 3.381 8.414 14.966a1 1 0 0 1-.167 1.199l-1.168 1.163a1 1 0 0 1-.706.291H6.351a1 1 0 0 1-.625-.219L3.25 18.8a1 1 0 0 1 .631-1.781l4.165.027"}]],kv=[["path",{d:"M12 3v6"}],["path",{d:"M16.76 3a2 2 0 0 1 1.8 1.1l2.23 4.479a2 2 0 0 1 .21.891V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9.472a2 2 0 0 1 .211-.894L5.45 4.1A2 2 0 0 1 7.24 3z"}],["path",{d:"M3.054 9.013h17.893"}]],Pv=[["path",{d:"M12 22V12"}],["path",{d:"M16 17h6"}],["path",{d:"M21 13V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l1.675-.955"}],["path",{d:"M3.29 7 12 12l8.71-5"}],["path",{d:"m7.5 4.27 8.997 5.148"}]],Bv=[["path",{d:"M12 22V12"}],["path",{d:"m16 17 2 2 4-4"}],["path",{d:"M21 11.127V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l1.32-.753"}],["path",{d:"M3.29 7 12 12l8.71-5"}],["path",{d:"m7.5 4.27 8.997 5.148"}]],zv=[["path",{d:"M12 22v-9"}],["path",{d:"M15.17 2.21a1.67 1.67 0 0 1 1.63 0L21 4.57a1.93 1.93 0 0 1 0 3.36L8.82 14.79a1.655 1.655 0 0 1-1.64 0L3 12.43a1.93 1.93 0 0 1 0-3.36z"}],["path",{d:"M20 13v3.87a2.06 2.06 0 0 1-1.11 1.83l-6 3.08a1.93 1.93 0 0 1-1.78 0l-6-3.08A2.06 2.06 0 0 1 4 16.87V13"}],["path",{d:"M21 12.43a1.93 1.93 0 0 0 0-3.36L8.83 2.2a1.64 1.64 0 0 0-1.63 0L3 4.57a1.93 1.93 0 0 0 0 3.36l12.18 6.86a1.636 1.636 0 0 0 1.63 0z"}]],Dv=[["path",{d:"M12 22V12"}],["path",{d:"M16 17h6"}],["path",{d:"M19 14v6"}],["path",{d:"M21 10.535V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l1.675-.955"}],["path",{d:"M3.29 7 12 12l8.71-5"}],["path",{d:"m7.5 4.27 8.997 5.148"}]],Fv=[["path",{d:"M12 22V12"}],["path",{d:"M20.27 18.27 22 20"}],["path",{d:"M21 10.498V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l.98-.559"}],["path",{d:"M3.29 7 12 12l8.71-5"}],["path",{d:"m7.5 4.27 8.997 5.148"}],["circle",{cx:"18.5",cy:"16.5",r:"2.5"}]],Rv=[["path",{d:"M12 22V12"}],["path",{d:"m16.5 14.5 5 5"}],["path",{d:"m16.5 19.5 5-5"}],["path",{d:"M21 10.5V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l.13-.074"}],["path",{d:"M3.29 7 12 12l8.71-5"}],["path",{d:"m7.5 4.27 8.997 5.148"}]],bv=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"}],["path",{d:"M12 22V12"}],["polyline",{points:"3.29 7 12 12 20.71 7"}],["path",{d:"m7.5 4.27 9 5.15"}]],Tv=[["path",{d:"M11 7 6 2"}],["path",{d:"M18.992 12H2.041"}],["path",{d:"M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595"}],["path",{d:"m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33"}]],qv=[["rect",{width:"16",height:"6",x:"2",y:"2",rx:"2"}],["path",{d:"M10 16v-2a2 2 0 0 1 2-2h8a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"}],["rect",{width:"4",height:"6",x:"8",y:"16",rx:"1"}]],Uv=[["path",{d:"m14.622 17.897-10.68-2.913"}],["path",{d:"M18.376 2.622a1 1 0 1 1 3.002 3.002L17.36 9.643a.5.5 0 0 0 0 .707l.944.944a2.41 2.41 0 0 1 0 3.408l-.944.944a.5.5 0 0 1-.707 0L8.354 7.348a.5.5 0 0 1 0-.707l.944-.944a2.41 2.41 0 0 1 3.408 0l.944.944a.5.5 0 0 0 .707 0z"}],["path",{d:"M9 8c-1.804 2.71-3.97 3.46-6.583 3.948a.507.507 0 0 0-.302.819l7.32 8.883a1 1 0 0 0 1.185.204C12.735 20.405 16 16.792 16 15"}]],J2=[["path",{d:"M10 2v2"}],["path",{d:"M14 2v4"}],["path",{d:"M17 2a1 1 0 0 1 1 1v9H6V3a1 1 0 0 1 1-1z"}],["path",{d:"M6 12a1 1 0 0 0-1 1v1a2 2 0 0 0 2 2h2a1 1 0 0 1 1 1v2.9a2 2 0 1 0 4 0V17a1 1 0 0 1 1-1h2a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1"}]],Ov=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor"}]],Gv=[["path",{d:"M11.25 17.25h1.5L12 18z"}],["path",{d:"m15 12 2 2"}],["path",{d:"M17.902 6.599a8 8 0 0 0-.5-.5"}],["path",{d:"M2 14.5C2 19.47 6.48 22 12 22s10-2.53 10-7.5a10 10 0 0 0-1.3-4.83 4.5 4.5 0 1 0-7.05-5.5 8 8 0 0 0-3.3 0 4.5 4.5 0 1 0-7.04 5.5A10 10 0 0 0 2 14.5"}],["path",{d:"M6.099 6.599a8 8 0 0 1 .5-.5"}],["path",{d:"m9 12-2 2"}]],Zv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 15h18"}],["path",{d:"m15 8-3 3-3-3"}]],Q2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M14 15h1"}],["path",{d:"M19 15h2"}],["path",{d:"M3 15h2"}],["path",{d:"M9 15h1"}]],Ev=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 15h18"}],["path",{d:"m9 10 3-3 3 3"}]],Wv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 15h18"}]],j2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m16 15-3-3 3-3"}]],Y2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 14v1"}],["path",{d:"M9 19v2"}],["path",{d:"M9 3v2"}],["path",{d:"M9 9v1"}]],_2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m14 9 3 3-3 3"}]],Iv=[["path",{d:"M15 10V9"}],["path",{d:"M15 15v-1"}],["path",{d:"M15 21v-2"}],["path",{d:"M15 5V3"}],["path",{d:"M9 10V9"}],["path",{d:"M9 15v-1"}],["path",{d:"M9 21v-2"}],["path",{d:"M9 5V3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],x2=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}]],Xv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M15 3v18"}],["path",{d:"m8 9 3 3-3 3"}]],a0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M15 14v1"}],["path",{d:"M15 19v2"}],["path",{d:"M15 3v2"}],["path",{d:"M15 9v1"}]],Nv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M15 3v18"}],["path",{d:"m10 15-3-3 3-3"}]],Kv=[["path",{d:"M14 15h1"}],["path",{d:"M14 9h1"}],["path",{d:"M19 15h2"}],["path",{d:"M19 9h2"}],["path",{d:"M3 15h2"}],["path",{d:"M3 9h2"}],["path",{d:"M9 15h1"}],["path",{d:"M9 9h1"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],Jv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M15 3v18"}]],Qv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"m9 16 3-3 3 3"}]],h0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M14 9h1"}],["path",{d:"M19 9h2"}],["path",{d:"M3 9h2"}],["path",{d:"M9 9h1"}]],jv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"m15 14-3 3-3-3"}]],Yv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}]],_v=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"M9 15h12"}]],xv=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 15h12"}],["path",{d:"M15 3v18"}]],t0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M9 21V9"}]],a$=[["path",{d:"M5.364 3.848C4 6 3 9.652 3 12.652V19a2 2 0 002 2h14a2 2 0 002-2v-5c0-2.334-1.816-4.668-2.622-7.002"}],["path",{d:"M7 3h11.379a2 2 0 011.789 1.106l.723 1.447A1 1 0 0119.997 7h-8.525a2 2 0 01-1.789-1.106L8.79 4.105a2 2 0 10-3.579 1.789l2.261 4.522A5 5 0 018 12.652V21"}]],h$=[["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551"}]],t$=[["path",{d:"M12.5 11.134 18.196 21"}],["path",{d:"M20.425 5.299a10 10 0 0 0-16.941 9.78c.183.563.843.774 1.355.478L20.16 6.711c.512-.296.66-.973.264-1.413"}],["path",{d:"M21 21H3"}]],d$=[["path",{d:"M8 21s-4-3-4-9 4-9 4-9"}],["path",{d:"M16 3s4 3 4 9-4 9-4 9"}]],c$=[["path",{d:"M12 18h10"}],["path",{d:"M13.248 9.998A4.5 4.5 0 0 0 11.75 8.6V8a1 1 0 0 0-7.5 0 4.9 4.9 0 0 0 2.25 9H8"}],["path",{d:"m15 14-2 6"}],["path",{d:"m19 14 2 6"}],["path",{d:"M21 14h-8"}],["path",{d:"M8 20v-5.922a2 2 0 0 0-.586-1.414L6.5 11.75"}],["path",{d:"M9.205 12.795 8 14"}],["circle",{cx:"19",cy:"6",r:"2"}]],M$=[["path",{d:"M11 15h2"}],["path",{d:"M12 12v3"}],["path",{d:"M12 19v3"}],["path",{d:"M15.282 19a1 1 0 0 0 .948-.68l2.37-6.988a7 7 0 1 0-13.2 0l2.37 6.988a1 1 0 0 0 .948.68z"}],["path",{d:"M9 9a3 3 0 1 1 6 0"}]],p$=[["path",{d:"M5.8 11.3 2 22l10.7-3.79"}],["path",{d:"M4 3h.01"}],["path",{d:"M22 8h.01"}],["path",{d:"M15 2h.01"}],["path",{d:"M22 20h.01"}],["path",{d:"m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 10"}],["path",{d:"m22 13-.82-.33c-.86-.34-1.82.2-1.98 1.11c-.11.7-.72 1.22-1.43 1.22H17"}],["path",{d:"m11 2 .33.82c.34.86-.2 1.82-1.11 1.98C9.52 4.9 9 5.52 9 6.23V7"}],["path",{d:"M11 13c1.93 1.93 2.83 4.17 2 5-.83.83-3.07-.07-5-2-1.93-1.93-2.83-4.17-2-5 .83-.83 3.07.07 5 2Z"}]],l$=[["rect",{x:"14",y:"3",width:"5",height:"18",rx:"1"}],["rect",{x:"5",y:"3",width:"5",height:"18",rx:"1"}]],i$=[["circle",{cx:"11",cy:"4",r:"2"}],["circle",{cx:"18",cy:"8",r:"2"}],["circle",{cx:"20",cy:"16",r:"2"}],["path",{d:"M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"}]],n$=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2"}],["path",{d:"M15 14h.01"}],["path",{d:"M9 6h6"}],["path",{d:"M9 10h6"}]],d0=[["path",{d:"M13 21h8"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}]],e$=[["path",{d:"m10 10-6.157 6.162a2 2 0 0 0-.5.833l-1.322 4.36a.5.5 0 0 0 .622.624l4.358-1.323a2 2 0 0 0 .83-.5L14 13.982"}],["path",{d:"m12.829 7.172 4.359-4.346a1 1 0 1 1 3.986 3.986l-4.353 4.353"}],["path",{d:"m2 2 20 20"}]],r$=[["path",{d:"m18 13-.654-4.736a4 4 0 00-2.756-3.266l-9.163-2.9a2 2 0 00-2.017.493l-.82.819a2 2 0 00-.492 2.017l2.9 9.163a4 4 0 003.266 2.756L13 18"}],["path",{d:"M18.648 12.352a1.205 1.205 0 011.704 0l1.296 1.296a1.205 1.205 0 010 1.704l-6.296 6.296a1.205 1.205 0 01-1.704 0l-1.296-1.296a1.205 1.205 0 010-1.704z"}],["path",{d:"m3 3 6.586 6.586"}],["circle",{cx:"11",cy:"11",r:"2"}]],c0=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}]],o$=[["path",{d:"M13 21h8"}],["path",{d:"m15 5 4 4"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}]],v$=[["path",{d:"m10 10-6.157 6.162a2 2 0 0 0-.5.833l-1.322 4.36a.5.5 0 0 0 .622.624l4.358-1.323a2 2 0 0 0 .83-.5L14 13.982"}],["path",{d:"m12.829 7.172 4.359-4.346a1 1 0 1 1 3.986 3.986l-4.353 4.353"}],["path",{d:"m15 5 4 4"}],["path",{d:"m2 2 20 20"}]],$$=[["path",{d:"M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"}],["path",{d:"m8 6 2-2"}],["path",{d:"m18 16 2-2"}],["path",{d:"m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]],m$=[["path",{d:"M10 3H8"}],["path",{d:"m15.007 5.008 3.987 3.986"}],["path",{d:"M20 15v4"}],["path",{d:"M21.174 6.813a2.82 2.82 0 0 0-3.986-3.987L3.842 16.175a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"M22 17h-4"}],["path",{d:"M4 5v4"}],["path",{d:"M6 7H2"}],["path",{d:"M9 2v2"}]],y$=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]],s$=[["path",{d:"M10.83 2.38a2 2 0 0 1 2.34 0l8 5.74a2 2 0 0 1 .73 2.25l-3.04 9.26a2 2 0 0 1-1.9 1.37H7.04a2 2 0 0 1-1.9-1.37L2.1 10.37a2 2 0 0 1 .73-2.25z"}]],C$=[["line",{x1:"19",x2:"5",y1:"5",y2:"19"}],["circle",{cx:"6.5",cy:"6.5",r:"2.5"}],["circle",{cx:"17.5",cy:"17.5",r:"2.5"}]],u$=[["path",{d:"M 5 22 H 11.072"}],["path",{d:"M15 5 C 23 4 23.328 12.676 20 14.536"}],["path",{d:"M15.802 10.308 C 14.776 9.465 14 8.496 14 7"}],["path",{d:"M2 2h.01"}],["path",{d:"M2 7h.01"}],["path",{d:"M5 4.5h.01"}],["path",{d:"M8.127 22 A 8 8 0 0 1 4 15 3 3 0 0 1 4.879 12.879C 6.879 10.879 10 10 10 7"}],["circle",{cx:"18",cy:"18",r:"4"}],["rect",{x:"9",y:"2",width:"6",height:"5",rx:"1"}]],g$=[["path",{d:"M12 2v20"}],["circle",{cx:"12",cy:"12",r:"7"}]],A$=[["circle",{cx:"12",cy:"5",r:"1"}],["path",{d:"m9 20 3-6 3 6"}],["path",{d:"m6 8 6 2 6-2"}],["path",{d:"M12 10v4"}]],H$=[["path",{d:"M20 11H4"}],["path",{d:"M20 7H4"}],["path",{d:"M7 21V4a1 1 0 0 1 1-1h4a1 1 0 0 1 0 12H7"}]],V$=[["path",{d:"M13 2a9 9 0 0 1 9 9"}],["path",{d:"M13 6a5 5 0 0 1 5 5"}],["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],S$=[["path",{d:"M16 2v6h6"}],["path",{d:"m22 2-6 6"}],["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],w$=[["path",{d:"M14 6h8"}],["path",{d:"m18 2 4 4-4 4"}],["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],L$=[["path",{d:"m16 2 6 6"}],["path",{d:"m22 2-6 6"}],["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],f$=[["path",{d:"M13.832 16.568a1 1 0 001.213-.303l.355-.465A2 2 0 0117 15h3a2 2 0 012 2v3a2 2 0 01-2 2A18 18 0 012 4a2 2 0 012-2h3a2 2 0 012 2v3a2 2 0 01-.8 1.6l-.468.351a1 1 0 00-.292 1.233 14 14 0 006.392 6.384"}],["path",{d:"M14.396 4.171h7.171"}],["path",{d:"M17.343 8.17h4.224"}]],k$=[["path",{d:"M10.1 13.9a14 14 0 0 0 3.732 2.668 1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2 18 18 0 0 1-12.728-5.272"}],["path",{d:"M22 2 2 22"}],["path",{d:"M4.76 13.582A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 .244.473"}]],P$=[["path",{d:"m16 8 6-6"}],["path",{d:"M22 8V2h-6"}],["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],B$=[["path",{d:"M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"}]],z$=[["line",{x1:"9",x2:"9",y1:"4",y2:"20"}],["path",{d:"M4 7c0-1.7 1.3-3 3-3h13"}],["path",{d:"M18 20c-1.7 0-3-1.3-3-3V4"}]],D$=[["path",{d:"M10 13v4"}],["path",{d:"M14 13v4"}],["path",{d:"M18 13v4"}],["path",{d:"M2 13h20"}],["path",{d:"M22 11.5A3.5 3.5 0 0018.5 8a3.52 3.52 0 01-3.173-2A7 7 0 002 9v10a2 2 0 002 2h16a2 2 0 002-2z"}],["path",{d:"M6 13v4"}]],F$=[["path",{d:"m14 13-8.381 8.38a1 1 0 0 1-3.001-3L11 9.999"}],["path",{d:"M15.973 4.027A13 13 0 0 0 5.902 2.373c-1.398.342-1.092 2.158.277 2.601a19.9 19.9 0 0 1 5.822 3.024"}],["path",{d:"M16.001 11.999a19.9 19.9 0 0 1 3.024 5.824c.444 1.369 2.26 1.676 2.603.278A13 13 0 0 0 20 8.069"}],["path",{d:"M18.352 3.352a1.205 1.205 0 0 0-1.704 0l-5.296 5.296a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l5.296-5.296a1.205 1.205 0 0 0 0-1.704z"}]],R$=[["path",{d:"M21 9V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h4"}],["rect",{width:"10",height:"7",x:"12",y:"13",rx:"2"}]],b$=[["path",{d:"M2 10h6V4"}],["path",{d:"m2 4 6 6"}],["path",{d:"M21 10V7a2 2 0 0 0-2-2h-7"}],["path",{d:"M3 14v2a2 2 0 0 0 2 2h3"}],["rect",{x:"12",y:"14",width:"10",height:"7",rx:"1"}]],T$=[["path",{d:"M11 17h3v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-3a3.16 3.16 0 0 0 2-2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-1a5 5 0 0 0-2-4V3a4 4 0 0 0-3.2 1.6l-.3.4H11a6 6 0 0 0-6 6v1a5 5 0 0 0 2 4v3a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"}],["path",{d:"M16 10h.01"}],["path",{d:"M2 8v1a2 2 0 0 0 2 2h1"}]],q$=[["path",{d:"M14 3v11"}],["path",{d:"M14 9h-3a3 3 0 0 1 0-6h9"}],["path",{d:"M18 3v11"}],["path",{d:"M22 18H2l4-4"}],["path",{d:"m6 22-4-4"}]],U$=[["path",{d:"M10 3v11"}],["path",{d:"M10 9H7a1 1 0 0 1 0-6h8"}],["path",{d:"M14 3v11"}],["path",{d:"m18 14 4 4H2"}],["path",{d:"m22 18-4 4"}]],O$=[["path",{d:"M13 4v16"}],["path",{d:"M17 4v16"}],["path",{d:"M19 4H9.5a4.5 4.5 0 0 0 0 9H13"}]],G$=[["path",{d:"M18 11h-4a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h4"}],["path",{d:"M6 7v13a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7"}],["rect",{width:"16",height:"5",x:"4",y:"2",rx:"1"}]],Z$=[["path",{d:"M12 17v5"}],["path",{d:"M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89"}],["path",{d:"m2 2 20 20"}],["path",{d:"M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11"}]],E$=[["path",{d:"m10.5 20.5 10-10a4.95 4.95 0 1 0-7-7l-10 10a4.95 4.95 0 1 0 7 7Z"}],["path",{d:"m8.5 8.5 7 7"}]],W$=[["path",{d:"M12 17v5"}],["path",{d:"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"}]],I$=[["path",{d:"m12 14-1 1"}],["path",{d:"m13.75 18.25-1.25 1.42"}],["path",{d:"M17.775 5.654a15.68 15.68 0 0 0-12.121 12.12"}],["path",{d:"M18.8 9.3a1 1 0 0 0 2.1 7.7"}],["path",{d:"M21.964 20.732a1 1 0 0 1-1.232 1.232l-18-5a1 1 0 0 1-.695-1.232A19.68 19.68 0 0 1 15.732 2.037a1 1 0 0 1 1.232.695z"}]],X$=[["path",{d:"M2 22h20"}],["path",{d:"M3.77 10.77 2 9l2-4.5 1.1.55c.55.28.9.84.9 1.45s.35 1.17.9 1.45L8 8.5l3-6 1.05.53a2 2 0 0 1 1.09 1.52l.72 5.4a2 2 0 0 0 1.09 1.52l4.4 2.2c.42.22.78.55 1.01.96l.6 1.03c.49.88-.06 1.98-1.06 2.1l-1.18.15c-.47.06-.95-.02-1.37-.24L4.29 11.15a2 2 0 0 1-.52-.38Z"}]],N$=[["path",{d:"m12 9-8.414 8.414A2 2 0 0 0 3 18.828v1.344a2 2 0 0 1-.586 1.414A2 2 0 0 1 3.828 21h1.344a2 2 0 0 0 1.414-.586L15 12"}],["path",{d:"m18 9 .4.4a1 1 0 1 1-3 3l-3.8-3.8a1 1 0 1 1 3-3l.4.4 3.4-3.4a1 1 0 1 1 3 3z"}],["path",{d:"m2 22 .414-.414"}]],K$=[["path",{d:"M2 22h20"}],["path",{d:"M6.36 17.4 4 17l-2-4 1.1-.55a2 2 0 0 1 1.8 0l.17.1a2 2 0 0 0 1.8 0L8 12 5 6l.9-.45a2 2 0 0 1 2.09.2l4.02 3a2 2 0 0 0 2.1.2l4.19-2.06a2.41 2.41 0 0 1 1.73-.17L21 7a1.4 1.4 0 0 1 .87 1.99l-.38.76c-.23.46-.6.84-1.07 1.08L7.58 17.2a2 2 0 0 1-1.22.18Z"}]],J$=[["path",{d:"M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"}]],Q$=[["path",{d:"M14 8.536V6a4 4 0 014-4h1.5a.5.5 0 01.5.5V4a4 4 0 01-4 4 4 4 0 00-4 4 5 5 0 01-8-4 5 5 0 018 4c0 2 1 3 1 5"}],["path",{d:"m18 17-1.085 3.58A2 2 0 0115 22H9.002a2 2 0 01-1.913-1.418L6 17"}],["path",{d:"M5 17h14"}]],j$=[["path",{d:"m10.215 4.56 9.79 5.71a2 2 0 0 1 .003 3.458l-.393.23"}],["path",{d:"m16.042 16.042-8.034 4.686A2 2 0 0 1 5 19V5"}],["path",{d:"m2 2 20 20"}]],Y$=[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"}]],_$=[["path",{d:"M12.832 8.445a1 1 0 00-1.589-.098l-2.075 3.098a1 1 0 000 1.11l2 3a1 1 0 001.664 0l2-3a1 1 0 000-1.11z"}],["rect",{x:"5",y:"2",width:"14",height:"20",rx:"2"}]],x$=[["path",{d:"M14.832 8.445a1 1 0 00-1.589-.098l-2.075 3.098a1 1 0 000 1.11l2 3a1 1 0 001.664 0l2-3a1 1 0 000-1.11z"}],["path",{d:"m7.18 20.827-5-11a2 2 0 01.993-2.647L7 5.44"}],["rect",{x:"7",y:"2",width:"14",height:"20",rx:"2"}]],am=[["path",{d:"M12.65 7.65a2 2 0 012.629-1.046l5.51 2.374a2 2 0 011.046 2.628l-3.957 9.184a2 2 0 01-2.628 1.046l-5.51-2.374a2 2 0 01-1.046-2.628z"}],["path",{d:"M18 7.777V4a2 2 0 00-2-2h-6a2 2 0 00-2 2v10a2 2 0 001.137 1.805"}],["path",{d:"m8 4.389-4.364.809a2 2 0 00-1.602 2.33l1.822 9.833a2 2 0 002.331 1.602l2.542-.47"}]],hm=[["path",{d:"M9 2v6"}],["path",{d:"M15 2v6"}],["path",{d:"M12 17v5"}],["path",{d:"M5 8h14"}],["path",{d:"M6 11V8h12v3a6 6 0 1 1-12 0Z"}]],M0=[["path",{d:"M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"}],["path",{d:"m2 22 3-3"}],["path",{d:"M7.5 13.5 10 11"}],["path",{d:"M10.5 16.5 13 14"}],["path",{d:"m18 3-4 4h6l-4 4"}]],tm=[["path",{d:"M12 22v-5"}],["path",{d:"M15 8V2"}],["path",{d:"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"}],["path",{d:"M9 8V2"}]],dm=[["path",{d:"M5 12h14"}],["path",{d:"M12 5v14"}]],cm=[["path",{d:"M3 2v1c0 1 2 1 2 2S3 6 3 7s2 1 2 2-2 1-2 2 2 1 2 2"}],["path",{d:"M18 6h.01"}],["path",{d:"M6 18h.01"}],["path",{d:"M20.83 8.83a4 4 0 0 0-5.66-5.66l-12 12a4 4 0 1 0 5.66 5.66Z"}],["path",{d:"M18 11.66V22a4 4 0 0 0 4-4V6"}]],Mm=[["path",{d:"M12 6V2h-1"}],["path",{d:"M9 15a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1"}],["path",{d:"M9 21V11a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v10"}]],pm=[["path",{d:"M10 4.5V4a2 2 0 0 0-2.41-1.957"}],["path",{d:"M13.9 8.4a2 2 0 0 0-1.26-1.295"}],["path",{d:"M21.7 16.2A8 8 0 0 0 22 14v-3a2 2 0 1 0-4 0v-1a2 2 0 0 0-3.63-1.158"}],["path",{d:"m7 15-1.8-1.8a2 2 0 0 0-2.79 2.86L6 19.7a7.74 7.74 0 0 0 6 2.3h2a8 8 0 0 0 5.657-2.343"}],["path",{d:"M6 6v8"}],["path",{d:"m2 2 20 20"}]],lm=[["path",{d:"M22 14a8 8 0 0 1-8 8"}],["path",{d:"M18 11v-1a2 2 0 0 0-2-2a2 2 0 0 0-2 2"}],["path",{d:"M14 10V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1"}],["path",{d:"M10 9.5V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v10"}],["path",{d:"M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"}]],im=[["path",{d:"M18 8a2 2 0 0 0 0-4 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0 0 4"}],["path",{d:"M10 22 9 8"}],["path",{d:"m14 22 1-14"}],["path",{d:"M20 8c.5 0 .9.4.8 1l-2.6 12c-.1.5-.7 1-1.2 1H7c-.6 0-1.1-.4-1.2-1L3.2 9c-.1-.6.3-1 .8-1Z"}]],nm=[["path",{d:"M18.6 14.4c.8-.8.8-2 0-2.8l-8.1-8.1a4.95 4.95 0 1 0-7.1 7.1l8.1 8.1c.9.7 2.1.7 2.9-.1Z"}],["path",{d:"m22 22-5.5-5.5"}]],em=[["path",{d:"M18 7c0-5.333-8-5.333-8 0"}],["path",{d:"M10 7v14"}],["path",{d:"M6 21h12"}],["path",{d:"M6 13h10"}]],rm=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15"}],["path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68"}],["path",{d:"M12 2v4"}],["path",{d:"m2 2 20 20"}]],om=[["path",{d:"M12 2v10"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04"}]],vm=[["path",{d:"M2 3h20"}],["path",{d:"M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3"}],["path",{d:"m7 21 5-5 5 5"}]],$m=[["path",{d:"M10 11v1"}],["path",{d:"M12 8h8"}],["path",{d:"M15 20v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3"}],["path",{d:"M4 20h16"}],["path",{d:"M4 22V4a2 2 0 012-2h12a2 2 0 012 2v18"}],["path",{d:"M4 8h4"}],["path",{d:"M8.635 10.093A2 2 0 018 8.631V7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-.293.707l-1 1a1 1 0 01-1.414 0z"}]],mm=[["path",{d:"M13.5 22H7a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v.5"}],["path",{d:"m16 19 2 2 4-4"}],["path",{d:"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v2"}],["path",{d:"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6"}]],ym=[["path",{d:"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"}],["path",{d:"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6"}],["rect",{x:"6",y:"14",width:"12",height:"8",rx:"1"}]],sm=[["path",{d:"M12.531 22H7a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h6.377"}],["path",{d:"m16.5 16.5 5 5"}],["path",{d:"m16.5 21.5 5-5"}],["path",{d:"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1.5"}],["path",{d:"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6"}]],Cm=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M12 9v11"}],["path",{d:"M2 9h13a2 2 0 0 1 2 2v9"}]],um=[["path",{d:"M5 7 3 5"}],["path",{d:"M9 6V3"}],["path",{d:"m13 7 2-2"}],["circle",{cx:"9",cy:"13",r:"3"}],["path",{d:"M11.83 12H20a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.17"}],["path",{d:"M16 16h2"}]],gm=[["path",{d:"M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z"}]],Am=[["path",{d:"M2.5 16.88a1 1 0 0 1-.32-1.43l9-13.02a1 1 0 0 1 1.64 0l9 13.01a1 1 0 0 1-.32 1.44l-8.51 4.86a2 2 0 0 1-1.98 0Z"}],["path",{d:"M12 2v20"}]],Hm=[["rect",{width:"5",height:"5",x:"3",y:"3",rx:"1"}],["rect",{width:"5",height:"5",x:"16",y:"3",rx:"1"}],["rect",{width:"5",height:"5",x:"3",y:"16",rx:"1"}],["path",{d:"M21 16h-3a2 2 0 0 0-2 2v3"}],["path",{d:"M21 21v.01"}],["path",{d:"M12 7v3a2 2 0 0 1-2 2H7"}],["path",{d:"M3 12h.01"}],["path",{d:"M12 3h.01"}],["path",{d:"M12 16v.01"}],["path",{d:"M16 12h1"}],["path",{d:"M21 12v.01"}],["path",{d:"M12 21v-1"}]],Vm=[["path",{d:"M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z"}],["path",{d:"M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z"}]],Sm=[["path",{d:"M13 16a3 3 0 0 1 2.24 5"}],["path",{d:"M18 12h.01"}],["path",{d:"M18 21h-8a4 4 0 0 1-4-4 7 7 0 0 1 7-7h.2L9.6 6.4a1 1 0 1 1 2.8-2.8L15.8 7h.2c3.3 0 6 2.7 6 6v1a2 2 0 0 1-2 2h-1a3 3 0 0 0-3 3"}],["path",{d:"M20 8.54V4a2 2 0 1 0-4 0v3"}],["path",{d:"M7.612 12.524a3 3 0 1 0-1.6 4.3"}]],wm=[["path",{d:"M12 12h.01"}],["path",{d:"M14 15.4641a4 4 0 0 1-4 0L7.52786 19.74597 A 1 1 0 0 0 7.99303 21.16211 10 10 0 0 0 16.00697 21.16211 1 1 0 0 0 16.47214 19.74597z"}],["path",{d:"M16 12a4 4 0 0 0-2-3.464l2.472-4.282a1 1 0 0 1 1.46-.305 10 10 0 0 1 4.006 6.94A1 1 0 0 1 21 12z"}],["path",{d:"M8 12a4 4 0 0 1 2-3.464L7.528 4.254a1 1 0 0 0-1.46-.305 10 10 0 0 0-4.006 6.94A1 1 0 0 0 3 12z"}]],Lm=[["path",{d:"M19.07 4.93A10 10 0 0 0 6.99 3.34"}],["path",{d:"M4 6h.01"}],["path",{d:"M2.29 9.62A10 10 0 1 0 21.31 8.35"}],["path",{d:"M16.24 7.76A6 6 0 1 0 8.23 16.67"}],["path",{d:"M12 18h.01"}],["path",{d:"M17.99 11.66A6 6 0 0 1 15.77 16.67"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"m13.41 10.59 5.66-5.66"}]],fm=[["path",{d:"M3 12h3.28a1 1 0 0 1 .948.684l2.298 7.934a.5.5 0 0 0 .96-.044L13.82 4.771A1 1 0 0 1 14.792 4H21"}]],km=[["path",{d:"M13.414 13.414a2 2 0 1 1-2.828-2.828"}],["path",{d:"M16.247 7.761a6 6 0 0 1 1.744 4.572"}],["path",{d:"M19.075 4.933a10 10 0 0 1 2.234 10.72"}],["path",{d:"m2 2 20 20"}],["path",{d:"M4.925 19.067a10 10 0 0 1 0-14.134"}],["path",{d:"M7.753 16.239a6 6 0 0 1 0-8.478"}]],Pm=[["path",{d:"M5 16v2"}],["path",{d:"M19 16v2"}],["rect",{width:"20",height:"8",x:"2",y:"8",rx:"2"}],["path",{d:"M18 12h.01"}]],Bm=[["path",{d:"M4.9 16.1C1 12.2 1 5.8 4.9 1.9"}],["path",{d:"M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"}],["circle",{cx:"12",cy:"9",r:"2"}],["path",{d:"M16.2 4.8c2 2 2.26 5.11.8 7.47"}],["path",{d:"M19.1 1.9a9.96 9.96 0 0 1 0 14.1"}],["path",{d:"M9.5 18h5"}],["path",{d:"m8 22 4-11 4 11"}]],zm=[["path",{d:"M16.247 7.761a6 6 0 0 1 0 8.478"}],["path",{d:"M19.075 4.933a10 10 0 0 1 0 14.134"}],["path",{d:"M4.925 19.067a10 10 0 0 1 0-14.134"}],["path",{d:"M7.753 16.239a6 6 0 0 1 0-8.478"}],["circle",{cx:"12",cy:"12",r:"2"}]],Dm=[["path",{d:"M20.34 17.52a10 10 0 1 0-2.82 2.82"}],["circle",{cx:"19",cy:"19",r:"2"}],["path",{d:"m13.41 13.41 4.18 4.18"}],["circle",{cx:"12",cy:"12",r:"2"}]],Fm=[["path",{d:"M22 17a10 10 0 0 0-20 0"}],["path",{d:"M6 17a6 6 0 0 1 12 0"}],["path",{d:"M10 17a2 2 0 0 1 4 0"}]],Rm=[["rect",{width:"12",height:"20",x:"6",y:"2",rx:"2"}],["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2"}]],bm=[["path",{d:"M13 22H4a2 2 0 0 1 0-4h12"}],["path",{d:"M13.236 18a3 3 0 0 0-2.2-5"}],["path",{d:"M16 9h.01"}],["path",{d:"M16.82 3.94a3 3 0 1 1 3.237 4.868l1.815 2.587a1.5 1.5 0 0 1-1.5 2.1l-2.872-.453a3 3 0 0 0-3.5 3"}],["path",{d:"M17 4.988a3 3 0 1 0-5.2 2.052A7 7 0 0 0 4 14.015 4 4 0 0 0 8 18"}]],Tm=[["path",{d:"M12 7v10"}],["path",{d:"M14.828 14.829a4 4 0 0 1-5.656 0 4 4 0 0 1 0-5.657 4 4 0 0 1 5.656 0"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}]],qm=[["path",{d:"M15.828 14.829a4 4 0 0 1-5.656 0 4 4 0 0 1 0-5.657 4 4 0 0 1 5.656 0"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M8 12h5"}]],Um=[["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M8 11h8"}],["path",{d:"M8 7h8"}],["path",{d:"M9 7a4 4 0 0 1 0 8H8l3 2"}]],Om=[["path",{d:"m12 10 3-3"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M9 11h6"}],["path",{d:"M9 15h6"}],["path",{d:"m9 7 3 3v7"}]],Gm=[["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M8 11h5a2 2 0 0 0 0-4h-3v10"}],["path",{d:"M8 15h5"}]],Zm=[["path",{d:"M10 17V9.5a1 1 0 0 1 5 0"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M8 13h5"}],["path",{d:"M8 17h7"}]],Em=[["path",{d:"M10 11h4"}],["path",{d:"M10 17V7h5"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}],["path",{d:"M8 15h5"}]],Wm=[["path",{d:"M13 16H8"}],["path",{d:"M14 8H8"}],["path",{d:"M16 12H8"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}]],Im=[["path",{d:"M10 7v10a5 5 0 0 0 5-5"}],["path",{d:"m14 8-6 3"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}]],Xm=[["path",{d:"M12 17V7"}],["path",{d:"M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8"}],["path",{d:"M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z"}]],Nm=[["path",{d:"M14 4v16H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z"}],["circle",{cx:"14",cy:"12",r:"8"}]],p0=[["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2"}],["path",{d:"M12 12h.01"}],["path",{d:"M17 12h.01"}],["path",{d:"M7 12h.01"}]],Km=[["path",{d:"M20 6a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-4a2 2 0 0 1-1.6-.8l-1.6-2.13a1 1 0 0 0-1.6 0L9.6 17.2A2 2 0 0 1 8 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"}]],Jm=[["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2"}]],Qm=[["rect",{width:"12",height:"20",x:"6",y:"2",rx:"2"}]],jm=[["path",{d:"M7 19H4.815a1.83 1.83 0 0 1-1.57-.881 1.785 1.785 0 0 1-.004-1.784L7.196 9.5"}],["path",{d:"M11 19h8.203a1.83 1.83 0 0 0 1.556-.89 1.784 1.784 0 0 0 0-1.775l-1.226-2.12"}],["path",{d:"m14 16-3 3 3 3"}],["path",{d:"M8.293 13.596 7.196 9.5 3.1 10.598"}],["path",{d:"m9.344 5.811 1.093-1.892A1.83 1.83 0 0 1 11.985 3a1.784 1.784 0 0 1 1.546.888l3.943 6.843"}],["path",{d:"m13.378 9.633 4.096 1.098 1.097-4.096"}]],Ym=[["path",{d:"m15 14 5-5-5-5"}],["path",{d:"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13"}]],_m=[["circle",{cx:"12",cy:"17",r:"1"}],["path",{d:"M21 7v6h-6"}],["path",{d:"M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"}]],xm=[["path",{d:"M21 7v6h-6"}],["path",{d:"M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"}]],ay=[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"}],["path",{d:"M16 16h5v5"}],["circle",{cx:"12",cy:"12",r:"1"}]],hy=[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"}],["path",{d:"M16 16h5v5"}]],ty=[["path",{d:"M21 8L18.74 5.74A9.75 9.75 0 0 0 12 3C11 3 10.03 3.16 9.13 3.47"}],["path",{d:"M8 16H3v5"}],["path",{d:"M3 12C3 9.51 4 7.26 5.64 5.64"}],["path",{d:"m3 16 2.26 2.26A9.75 9.75 0 0 0 12 21c2.49 0 4.74-1 6.36-2.64"}],["path",{d:"M21 12c0 1-.16 1.97-.47 2.87"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M22 22 2 2"}]],dy=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]],cy=[["path",{d:"M5 6a4 4 0 0 1 4-4h6a4 4 0 0 1 4 4v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6Z"}],["path",{d:"M5 10h14"}],["path",{d:"M15 7v6"}]],My=[["path",{d:"M4 7V4h16v3"}],["path",{d:"M5 20h6"}],["path",{d:"M13 4 8 20"}],["path",{d:"m15 15 5 5"}],["path",{d:"m20 15-5 5"}]],py=[["path",{d:"M17 3v10"}],["path",{d:"m12.67 5.5 8.66 5"}],["path",{d:"m12.67 10.5 8.66-5"}],["path",{d:"M9 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2z"}]],ly=[["path",{d:"m2 9 3-3 3 3"}],["path",{d:"M13 18H7a2 2 0 0 1-2-2V6"}],["path",{d:"m22 15-3 3-3-3"}],["path",{d:"M11 6h6a2 2 0 0 1 2 2v10"}]],iy=[["path",{d:"m17 2 4 4-4 4"}],["path",{d:"M3 11v-1a4 4 0 0 1 4-4h14"}],["path",{d:"m7 22-4-4 4-4"}],["path",{d:"M21 13v1a4 4 0 0 1-4 4H3"}],["path",{d:"M11 10h1v4"}]],ny=[["path",{d:"M11.656 6H21l-4-4"}],["path",{d:"M17.898 17.898A4 4 0 0 1 17 18H3l4-4"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 13v1a4 4 0 0 1-.171 1.159"}],["path",{d:"m21 6-4 4"}],["path",{d:"M3 11v-1a4 4 0 0 1 3.102-3.898"}],["path",{d:"m7 22-4-4"}]],ey=[["path",{d:"m17 2 4 4-4 4"}],["path",{d:"M3 11v-1a4 4 0 0 1 4-4h14"}],["path",{d:"m7 22-4-4 4-4"}],["path",{d:"M21 13v1a4 4 0 0 1-4 4H3"}]],ry=[["path",{d:"M14 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1"}],["path",{d:"M14 4a1 1 0 0 1 1-1"}],["path",{d:"M15 10a1 1 0 0 1-1-1"}],["path",{d:"M19 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1"}],["path",{d:"M21 4a1 1 0 0 0-1-1"}],["path",{d:"M21 9a1 1 0 0 1-1 1"}],["path",{d:"m3 7 3 3 3-3"}],["path",{d:"M6 10V5a2 2 0 0 1 2-2h2"}],["rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}]],oy=[["path",{d:"M14 4a1 1 0 0 1 1-1"}],["path",{d:"M15 10a1 1 0 0 1-1-1"}],["path",{d:"M21 4a1 1 0 0 0-1-1"}],["path",{d:"M21 9a1 1 0 0 1-1 1"}],["path",{d:"m3 7 3 3 3-3"}],["path",{d:"M6 10V5a2 2 0 0 1 2-2h2"}],["rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}]],vy=[["path",{d:"m12 17-5-5 5-5"}],["path",{d:"M22 18v-2a4 4 0 0 0-4-4H7"}],["path",{d:"m7 17-5-5 5-5"}]],$y=[["path",{d:"M20 18v-2a4 4 0 0 0-4-4H4"}],["path",{d:"m9 17-5-5 5-5"}]],my=[["path",{d:"M12 6a2 2 0 0 0-3.414-1.414l-6 6a2 2 0 0 0 0 2.828l6 6A2 2 0 0 0 12 18z"}],["path",{d:"M22 6a2 2 0 0 0-3.414-1.414l-6 6a2 2 0 0 0 0 2.828l6 6A2 2 0 0 0 22 18z"}]],yy=[["path",{d:"M12 11.22C11 9.997 10 9 10 8a2 2 0 0 1 4 0c0 1-.998 2.002-2.01 3.22"}],["path",{d:"m12 18 2.57-3.5"}],["path",{d:"M6.243 9.016a7 7 0 0 1 11.507-.009"}],["path",{d:"M9.35 14.53 12 11.22"}],["path",{d:"M9.35 14.53C7.728 12.246 6 10.221 6 7a6 5 0 0 1 12 0c-.005 3.22-1.778 5.235-3.43 7.5l3.557 4.527a1 1 0 0 1-.203 1.43l-1.894 1.36a1 1 0 0 1-1.384-.215L12 18l-2.679 3.593a1 1 0 0 1-1.39.213l-1.865-1.353a1 1 0 0 1-.203-1.422z"}]],sy=[["path",{d:"M12 17v4"}],["path",{d:"M12 5V3"}],["path",{d:"M12 9v3"}],["path",{d:"M2.077 18.449A2 2 0 0 0 4 21h16a2 2 0 0 0 1.924-2.55l-4-14A2 2 0 0 0 16 3H8a2 2 0 0 0-1.924 1.45z"}]],Cy=[["path",{d:"M12 21 7.5 8.322"}],["path",{d:"m14 7 1.75-3.767a.5.5 0 0 1 .662-.172L20 5.005"}],["path",{d:"m20 8.998-3.588 1.944a.5.5 0 0 1-.662-.172L14 7H8"}],["path",{d:"M3.486 21h10"}],["path",{d:"M5 21V8.732"}],["circle",{cx:"6",cy:"7",r:"2"}]],uy=[["path",{d:"M11 17h2"}],["path",{d:"M12 12h.01"}],["path",{d:"M17 12a5 5 0 00-10 0"}],["path",{d:"M19 2v2.8"}],["path",{d:"M2 5h2.8"}],["path",{d:"M22 5h-2.8"}],["path",{d:"M5 2v2.8"}],["circle",{cx:"12",cy:"12",r:"10"}]],gy=[["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"}],["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09"}],["path",{d:"M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05"}]],Ay=[["path",{d:"m15 13 3.708 7.416"}],["path",{d:"M3 19a15 15 0 0 0 18 0"}],["path",{d:"m3 2 3.21 9.633A2 2 0 0 0 8.109 13H18"}],["path",{d:"m9 13-3.708 7.416"}]],Hy=[["path",{d:"M6 19V5"}],["path",{d:"M10 19V6.8"}],["path",{d:"M14 19v-7.8"}],["path",{d:"M18 5v4"}],["path",{d:"M18 19v-6"}],["path",{d:"M22 19V9"}],["path",{d:"M2 19V9a4 4 0 0 1 4-4c2 0 4 1.33 6 4s4 4 6 4a4 4 0 1 0-3-6.65"}]],Vy=[["path",{d:"M17 10h-1a4 4 0 1 1 4-4v.534"}],["path",{d:"M17 6h1a4 4 0 0 1 1.42 7.74l-2.29.87a6 6 0 0 1-5.339-10.68l2.069-1.31"}],["path",{d:"M4.5 17c2.8-.5 4.4 0 5.5.8s1.8 2.2 2.3 3.7c-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2"}],["path",{d:"M9.77 12C4 15 2 22 2 22"}],["circle",{cx:"17",cy:"8",r:"2"}]],l0=[["path",{d:"m15.194 13.707 3.814 1.86-1.86 3.814"}],["path",{d:"M16.47214 7.52786 A 5 10 0 1 0 13 21.79796"}],["path",{d:"M21.79796 11 A 10 5 0 1 0 19 15.57071"}]],i0=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}],["path",{d:"M12 7v5l4 2"}]],Sy=[["path",{d:"M12 7v6"}],["path",{d:"M12 9h2"}],["path",{d:"M3 12a9 9 0 1 0 9-9 9.74 9.74 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}],["circle",{cx:"12",cy:"15",r:"2"}]],wy=[["path",{d:"M20 9V7a2 2 0 0 0-2-2h-6"}],["path",{d:"m15 2-3 3 3 3"}],["path",{d:"M20 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2"}]],Ly=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}]],fy=[["path",{d:"M12 7v5l4 2"}],["path",{d:"M16 8h5V3"}],["path",{d:"m21 8-2.3-2.3A9.7 9.7 0 0012 3a9 9 0 109 9"}]],ky=[["path",{d:"M12 3a9.75 9.75 0 0 1 6.74 2.74"}],["path",{d:"M18.74 5.74 21 8"}],["path",{d:"M21 8V3"}],["path",{d:"M7.5 19.794c-6-3.464-6-12.124 0-15.588"}],["path",{d:"M7.5 4.206A9 9 0 0 1 12 3"}],["path",{d:"M12 7v5l4 2"}],["path",{d:"M14 20.775A9 9 0 0 1 12 21"}],["path",{d:"M19 17.656a9 9 0 0 1-1.5 1.456"}],["path",{d:"M21 12a9 9 0 0 1-.228 2"}],["path",{d:"M21 8h-5"}]],Py=[["path",{d:"M12 5H6a2 2 0 0 0-2 2v3"}],["path",{d:"m9 8 3-3-3-3"}],["path",{d:"M4 14v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"}]],By=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}]],zy=[["circle",{cx:"6",cy:"19",r:"3"}],["path",{d:"M9 19h8.5c.4 0 .9-.1 1.3-.2"}],["path",{d:"M5.2 5.2A3.5 3.53 0 0 0 6.5 12H12"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 15.3a3.5 3.5 0 0 0-3.3-3.3"}],["path",{d:"M15 5h-4.3"}],["circle",{cx:"18",cy:"5",r:"3"}]],Dy=[["circle",{cx:"6",cy:"19",r:"3"}],["path",{d:"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15"}],["circle",{cx:"18",cy:"5",r:"3"}]],Fy=[["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2"}],["path",{d:"M6.01 18H6"}],["path",{d:"M10.01 18H10"}],["path",{d:"M15 10v4"}],["path",{d:"M17.84 7.17a4 4 0 0 0-5.66 0"}],["path",{d:"M20.66 4.34a8 8 0 0 0-11.31 0"}]],n0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 12h18"}]],e0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M21 9H3"}],["path",{d:"M21 15H3"}]],Ry=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M21 7.5H3"}],["path",{d:"M21 12H3"}],["path",{d:"M21 16.5H3"}]],by=[["path",{d:"M4 11a9 9 0 0 1 9 9"}],["path",{d:"M4 4a16 16 0 0 1 16 16"}],["circle",{cx:"5",cy:"19",r:"1"}]],Ty=[["path",{d:"m10 10 4 4"}],["path",{d:"m13 7 4 4"}],["path",{d:"M15.34 2.138A15 15 0 002.138 15.34c-.357 2.94.004 4.919.805 5.717.798.8 2.778 1.162 5.718.805A15 15 0 0021.862 8.661c.357-2.94-.004-4.92-.805-5.718-.798-.8-2.778-1.162-5.717-.805"}],["path",{d:"M17 7 7 17"}],["path",{d:"m7 13 4 4"}]],qy=[["path",{d:"M10 15v-3"}],["path",{d:"M14 15v-3"}],["path",{d:"M18 15v-3"}],["path",{d:"M2 8V4"}],["path",{d:"M22 6H2"}],["path",{d:"M22 8V4"}],["path",{d:"M6 15v-3"}],["rect",{x:"2",y:"12",width:"20",height:"8",rx:"2"}]],Uy=[["path",{d:"M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.41 2.41 0 0 1 0-3.4l2.6-2.6a2.41 2.41 0 0 1 3.4 0Z"}],["path",{d:"m14.5 12.5 2-2"}],["path",{d:"m11.5 9.5 2-2"}],["path",{d:"m8.5 6.5 2-2"}],["path",{d:"m17.5 15.5 2-2"}]],Oy=[["path",{d:"M6 11h8a4 4 0 0 0 0-8H9v18"}],["path",{d:"M6 15h8"}]],Gy=[["path",{d:"M19.496 12a2.5 2.5 0 00.399-2.214A2 2 0 0020.32 6.5 2 2 0 0019 3a2 2 0 00-1.5.68 2 2 0 00-3.287.424 2.5 2.5 0 00-3.189 2.06A3 3 0 0012 12l4-4"}],["path",{d:"M4 12a1 1 0 00-.99 1.133A9 9 0 0012 21a9 9 0 008.99-7.867A1 1 0 0020 12z"}],["path",{d:"M7 21h10"}],["path",{d:"M9.85 6.907A3.5 3.5 0 005.05 12"}]],Zy=[["path",{d:"m2.37 11.223 8.372-6.777a2 2 0 0 1 2.516 0l8.371 6.777"}],["path",{d:"M21 15a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-5.25"}],["path",{d:"M3 15a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h9"}],["path",{d:"m6.67 15 6.13 4.6a2 2 0 0 0 2.8-.4l3.15-4.2"}],["rect",{width:"20",height:"4",x:"2",y:"11",rx:"1"}]],Ey=[["path",{d:"M10 2v15"}],["path",{d:"M7 22a4 4 0 0 1-4-4 1 1 0 0 1 1-1h16a1 1 0 0 1 1 1 4 4 0 0 1-4 4z"}],["path",{d:"M9.159 2.46a1 1 0 0 1 1.521-.193l9.977 8.98A1 1 0 0 1 20 13H4a1 1 0 0 1-.824-1.567z"}]],Wy=[["path",{d:"M18 12a6 6 0 00-6-6"}],["path",{d:"M2.824 10.459a8 8 0 0010.717 10.717c.558-.276.623-1.012.183-1.452l-9.448-9.448c-.44-.44-1.176-.375-1.452.183"}],["path",{d:"M22 12A10 10 0 0012 2"}],["path",{d:"m9 15 4-4"}]],Iy=[["path",{d:"m13.5 6.5-3.148-3.148a1.205 1.205 0 0 0-1.704 0L6.352 5.648a1.205 1.205 0 0 0 0 1.704L9.5 10.5"}],["path",{d:"M16.5 7.5 19 5"}],["path",{d:"m17.5 10.5 3.148 3.148a1.205 1.205 0 0 1 0 1.704l-2.296 2.296a1.205 1.205 0 0 1-1.704 0L13.5 14.5"}],["path",{d:"M9 21a6 6 0 0 0-6-6"}],["path",{d:"M9.352 10.648a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l4.296-4.296a1.205 1.205 0 0 0 0-1.704l-2.296-2.296a1.205 1.205 0 0 0-1.704 0z"}]],Xy=[["path",{d:"m20 19.5-5.5 1.2"}],["path",{d:"M14.5 4v11.22a1 1 0 0 0 1.242.97L20 15.2"}],["path",{d:"m2.978 19.351 5.549-1.363A2 2 0 0 0 10 16V2"}],["path",{d:"M20 10 4 13.5"}]],Ny=[["path",{d:"M10 2v3a1 1 0 0 0 1 1h5"}],["path",{d:"M18 18v-6a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6"}],["path",{d:"M18 22H4a2 2 0 0 1-2-2V6"}],["path",{d:"M8 18a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9.172a2 2 0 0 1 1.414.586l2.828 2.828A2 2 0 0 1 22 6.828V16a2 2 0 0 1-2.01 2z"}]],Ky=[["path",{d:"M12.5 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h10.2a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4v4.35"}],["path",{d:"m16 19 2 2 4-4"}],["path",{d:"M17 15.13V14a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7"}]],Jy=[["path",{d:"M13 13H8a1 1 0 0 0-1 1v7"}],["path",{d:"M14 8h1"}],["path",{d:"M17 21v-4"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20.41 20.41A2 2 0 0 1 19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 .59-1.41"}],["path",{d:"M9 3h6.2a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V15"}]],Qy=[["path",{d:"M13.33 13H8a1 1 0 00-1 1v7"}],["path",{d:"M14.363 17.634a2 2 0 00-.506.854l-.837 2.87a.5.5 0 00.62.62l2.87-.837a2 2 0 00.854-.506l4.013-4.009a1 1 0 10-3.004-3.004z"}],["path",{d:"M7 3v4a1 1 0 001 1h7"}],["path",{d:"M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h10.2a2 2 0 011.4.6l3.8 3.8a2 2 0 01.6 1.4v.3"}]],jy=[["path",{d:"M12.5 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h10.2a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V12"}],["path",{d:"M16 13H8a1 1 0 0 0-1 1v7"}],["path",{d:"M19 22v-6"}],["path",{d:"M22 19h-6"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7"}]],r0=[["path",{d:"M5 7v11a1 1 0 0 0 1 1h11"}],["path",{d:"M5.293 18.707 11 13"}],["circle",{cx:"19",cy:"19",r:"2"}],["circle",{cx:"5",cy:"5",r:"2"}]],Yy=[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7"}]],_y=[["path",{d:"M12 3v18"}],["path",{d:"m19 8 3 8a5 5 0 0 1-6 0zV7"}],["path",{d:"M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1"}],["path",{d:"m5 8 3 8a5 5 0 0 1-6 0zV7"}],["path",{d:"M7 21h10"}]],xy=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M8 7v10"}],["path",{d:"M12 7v10"}],["path",{d:"M17 7v10"}]],as=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M14 15H9v-5"}],["path",{d:"M16 3h5v5"}],["path",{d:"M21 3 9 15"}]],hs=[["path",{d:"M12 12v5.5"}],["path",{d:"M17 3h2a2 2 0 012 2v2"}],["path",{d:"M21 17v2a2 2 0 01-2 2h-2"}],["path",{d:"M3 7V5a2 2 0 012-2h2"}],["path",{d:"M7 21H5a2 2 0 01-2-2v-2"}],["path",{d:"M7.264 9.252 12 12l4.737-2.748"}],["path",{d:"M7.995 8.514A2 2 0 007 10.244v3.516a2 2 0 00.996 1.73l3 1.74a2 2 0 002.008 0l3-1.74A2 2 0 0017 13.76v-3.517a2 2 0 00-.995-1.73l-3-1.742a2 2 0 00-1.892-.064z"}]],ts=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["circle",{cx:"12",cy:"12",r:"1"}],["path",{d:"M18.944 12.33a1 1 0 0 0 0-.66 7.5 7.5 0 0 0-13.888 0 1 1 0 0 0 0 .66 7.5 7.5 0 0 0 13.888 0"}]],ds=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M8 14s1.5 2 4 2 4-2 4-2"}],["path",{d:"M9 9h.01"}],["path",{d:"M15 9h.01"}]],cs=[["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M7.828 13.07A3 3 0 0 1 12 8.764a3 3 0 0 1 4.172 4.306l-3.447 3.62a1 1 0 0 1-1.449 0z"}]],Ms=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M7 12h10"}]],ps=[["path",{d:"M17 12v4a1 1 0 0 1-1 1h-4"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M17 8V7"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M7 17h.01"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["rect",{x:"7",y:"7",width:"5",height:"5",rx:"1"}]],ls=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["circle",{cx:"12",cy:"12",r:"3"}],["path",{d:"m16 16-1.9-1.9"}]],is=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["rect",{width:"8",height:"8",x:"8",y:"8",rx:"1"}]],ns=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M7 8h8"}],["path",{d:"M7 12h10"}],["path",{d:"M7 16h6"}]],es=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2"}]],rs=[["path",{d:"M14 21v-3a2 2 0 0 0-4 0v3"}],["path",{d:"M18 4.933V21"}],["path",{d:"m4 6 7.106-3.79a2 2 0 0 1 1.788 0L20 6"}],["path",{d:"m6 11-3.52 2.147a1 1 0 0 0-.48.854V19a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5a1 1 0 0 0-.48-.853L18 11"}],["path",{d:"M6 4.933V21"}],["circle",{cx:"12",cy:"9",r:"2"}]],os=[["path",{d:"M5.42 9.42 8 12"}],["circle",{cx:"4",cy:"8",r:"2"}],["path",{d:"m14 6-8.58 8.58"}],["circle",{cx:"4",cy:"16",r:"2"}],["path",{d:"M10.8 14.8 14 18"}],["path",{d:"M16 12h-2"}],["path",{d:"M22 12h-2"}]],vs=[["circle",{cx:"6",cy:"6",r:"3"}],["path",{d:"M8.12 8.12 12 12"}],["path",{d:"M20 4 8.12 15.88"}],["circle",{cx:"6",cy:"18",r:"3"}],["path",{d:"M14.8 14.8 20 20"}]],$s=[["path",{d:"M21 4h-3.5l2 11.05"}],["path",{d:"M6.95 17h5.142c.523 0 .95-.406 1.063-.916a6.5 6.5 0 0 1 5.345-5.009"}],["circle",{cx:"19.5",cy:"17.5",r:"2.5"}],["circle",{cx:"4.5",cy:"17.5",r:"2.5"}]],ms=[["path",{d:"M19 5a2 2 0 012 2v11a2 2 0 01-2 2h-5.586a1 1 0 00-.707.293l-1.414 1.414a1 1 0 01-.707.293H8.414a1 1 0 01-.707-.293l-1.414-1.414A1 1 0 005.586 20H5a2 2 0 01-2-2V5.286c0-.394.11-.785.36-1.09a6 6 0 019.168-.133C12.996 4.6 13.637 5 14.35 5z"}],["path",{d:"M21 12h-7.586a1 1 0 00-.707.293l-1.414 1.414a1 1 0 01-.707.293H8.414a1 1 0 01-.707-.293l-1.414-1.414A1 1 0 005.586 12H3"}]],ys=[["path",{d:"M13 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-3"}],["path",{d:"M8 21h8"}],["path",{d:"M12 17v4"}],["path",{d:"m22 3-5 5"}],["path",{d:"m17 3 5 5"}]],ss=[["path",{d:"M13 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-3"}],["path",{d:"M8 21h8"}],["path",{d:"M12 17v4"}],["path",{d:"m17 8 5-5"}],["path",{d:"M17 3h5v5"}]],Cs=[["path",{d:"M10 9v10"}],["path",{d:"M14 13v2"}],["path",{d:"M16.5 10.5 5.706 21.294A2.4 2.4 0 014.002 22H2.5a.5.5 0 01-.5-.5v-1.502a2.4 2.4 0 01.706-1.704L13.5 7.5"}],["path",{d:"M20.819 11.575a6 6 0 00.969-5.162.6.6 0 00-.981-.219l-.455.454a1.2 1.2 0 01-1.704 0l-1.296-1.296a1.2 1.2 0 010-1.704l.454-.453a.595.595 0 00-.219-.98 6 6 0 00-5.162.967 2 2 0 00-.222 3.019l5.596 5.597a2 2 0 003.02-.222"}],["path",{d:"M6 13v8"}]],us=[["path",{d:"M19 17V5a2 2 0 0 0-2-2H4"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"}]],gs=[["path",{d:"M15 12h-5"}],["path",{d:"M15 8h-5"}],["path",{d:"M19 17V5a2 2 0 0 0-2-2H4"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"}]],As=[["circle",{cx:"11",cy:"11",r:"8"}],["path",{d:"m21 21-4.3-4.3"}],["path",{d:"M11 7v4"}],["path",{d:"M11 15h.01"}]],Hs=[["path",{d:"m8 11 2 2 4-4"}],["circle",{cx:"11",cy:"11",r:"8"}],["path",{d:"m21 21-4.3-4.3"}]],Vs=[["path",{d:"m13 13.5 2-2.5-2-2.5"}],["path",{d:"m21 21-4.3-4.3"}],["path",{d:"M9 8.5 7 11l2 2.5"}],["circle",{cx:"11",cy:"11",r:"8"}]],Ss=[["path",{d:"m13.5 8.5-5 5"}],["circle",{cx:"11",cy:"11",r:"8"}],["path",{d:"m21 21-4.3-4.3"}]],ws=[["path",{d:"m13.5 8.5-5 5"}],["path",{d:"m8.5 8.5 5 5"}],["circle",{cx:"11",cy:"11",r:"8"}],["path",{d:"m21 21-4.3-4.3"}]],Ls=[["path",{d:"M16 5a4 3 0 0 0-8 0c0 4 8 3 8 7a4 3 0 0 1-8 0"}],["path",{d:"M8 19a4 3 0 0 0 8 0c0-4-8-3-8-7a4 3 0 0 1 8 0"}]],fs=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]],o0=[["path",{d:"M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z"}],["path",{d:"M6 12h16"}]],ks=[["rect",{x:"14",y:"14",width:"8",height:"8",rx:"2"}],["rect",{x:"2",y:"2",width:"8",height:"8",rx:"2"}],["path",{d:"M7 14v1a2 2 0 0 0 2 2h1"}],["path",{d:"M14 7h1a2 2 0 0 1 2 2v1"}]],Ps=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"}],["path",{d:"m21.854 2.147-10.94 10.939"}]],Bs=[["path",{d:"m16 16-4 4-4-4"}],["path",{d:"M3 12h18"}],["path",{d:"m8 8 4-4 4 4"}]],zs=[["path",{d:"M12 3v18"}],["path",{d:"m16 16 4-4-4-4"}],["path",{d:"m8 8-4 4 4 4"}]],Ds=[["path",{d:"m10.852 14.772-.383.923"}],["path",{d:"M13.148 14.772a3 3 0 1 0-2.296-5.544l-.383-.923"}],["path",{d:"m13.148 9.228.383-.923"}],["path",{d:"m13.53 15.696-.382-.924a3 3 0 1 1-2.296-5.544"}],["path",{d:"m14.772 10.852.923-.383"}],["path",{d:"m14.772 13.148.923.383"}],["path",{d:"M4.5 10H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-.5"}],["path",{d:"M4.5 14H4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2h-.5"}],["path",{d:"M6 18h.01"}],["path",{d:"M6 6h.01"}],["path",{d:"m9.228 10.852-.923-.383"}],["path",{d:"m9.228 13.148-.923.383"}]],Fs=[["path",{d:"M6 10H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2"}],["path",{d:"M6 14H4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2h-2"}],["path",{d:"M6 6h.01"}],["path",{d:"M6 18h.01"}],["path",{d:"m13 6-4 6h6l-4 6"}]],Rs=[["path",{d:"M10 16V8"}],["path",{d:"M14 16V8"}],["path",{d:"M18 16V8"}],["path",{d:"M6 12h.01"}],["path",{d:"M6 16h.01"}],["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}]],bs=[["path",{d:"M7 2h13a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-5"}],["path",{d:"M10 10 2.5 2.5C2 2 2 2.5 2 5v3a2 2 0 0 0 2 2h6z"}],["path",{d:"M22 17v-1a2 2 0 0 0-2-2h-1"}],["path",{d:"M4 14a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16.5l1-.5.5.5-8-8H4z"}],["path",{d:"M6 18h.01"}],["path",{d:"m2 2 20 20"}]],Ts=[["path",{d:"M12.5 10H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v2"}],["path",{d:"M16 12h6"}],["path",{d:"M19 9v6"}],["path",{d:"M22 18v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h8.5"}],["path",{d:"M6 18h.01"}],["path",{d:"M6 6h.01"}]],qs=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18"}]],Us=[["path",{d:"M14 17H5"}],["path",{d:"M19 7h-9"}],["circle",{cx:"17",cy:"17",r:"3"}],["circle",{cx:"7",cy:"7",r:"3"}]],Os=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"}],["circle",{cx:"12",cy:"12",r:"3"}]],Gs=[["path",{d:"M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z"}],["rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}],["circle",{cx:"17.5",cy:"17.5",r:"3.5"}]],Zs=[["path",{d:"M12 2v13"}],["path",{d:"m16 6-4-4-4 4"}],["path",{d:"M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"}]],Es=[["circle",{cx:"18",cy:"5",r:"3"}],["circle",{cx:"6",cy:"12",r:"3"}],["circle",{cx:"18",cy:"19",r:"3"}],["line",{x1:"8.59",x2:"15.42",y1:"13.51",y2:"17.49"}],["line",{x1:"15.41",x2:"8.59",y1:"6.51",y2:"10.49"}]],Ws=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["line",{x1:"3",x2:"21",y1:"9",y2:"9"}],["line",{x1:"3",x2:"21",y1:"15",y2:"15"}],["line",{x1:"9",x2:"9",y1:"9",y2:"21"}],["line",{x1:"15",x2:"15",y1:"9",y2:"21"}]],Is=[["path",{d:"M14 11a2 2 0 1 1-4 0 4 4 0 0 1 8 0 6 6 0 0 1-12 0 8 8 0 0 1 16 0 10 10 0 1 1-20 0 11.93 11.93 0 0 1 2.42-7.22 2 2 0 1 1 3.16 2.44"}]],Xs=[["path",{d:"M12 12V9a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3"}],["path",{d:"M16 20v-3a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3"}],["path",{d:"M20 22V2"}],["path",{d:"M4 12h16"}],["path",{d:"M4 20h16"}],["path",{d:"M4 2v20"}],["path",{d:"M4 4h16"}]],Ns=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M12 8v4"}],["path",{d:"M12 16h.01"}]],Ks=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"m4.243 5.21 14.39 12.472"}]],Js=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"m9 12 2 2 4-4"}]],Qs=[["path",{d:"M11 22c-3.806-1.45-7-3.966-7-9V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v4"}],["path",{d:"M14.923 16.547 14 16.164"}],["path",{d:"m14.923 18.843-.923.383"}],["path",{d:"M16.547 14.923 16.164 14"}],["path",{d:"m16.547 20.467-.383.924"}],["path",{d:"m18.843 14.923.383-.923"}],["path",{d:"m19.225 21.391-.382-.924"}],["path",{d:"m20.467 16.547.923-.383"}],["path",{d:"m20.467 18.843.923.383"}],["circle",{cx:"17.695",cy:"17.695",r:"3"}]],js=[["path",{d:"m10.929 14.467-.383.924"}],["path",{d:"M10.929 8.923 10.546 8"}],["path",{d:"M13.225 8.923 13.608 8"}],["path",{d:"m13.607 15.391-.382-.924"}],["path",{d:"m14.849 10.547.923-.383"}],["path",{d:"m14.849 12.843.923.383"}],["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"m9.305 10.547-.923-.383"}],["path",{d:"m9.305 12.843-.923.383"}],["circle",{cx:"12.077",cy:"11.695",r:"3"}]],Ys=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M8 12h.01"}],["path",{d:"M12 12h.01"}],["path",{d:"M16 12h.01"}]],_s=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M12 22V2"}]],xs=[["path",{d:"M12 13v3"}],["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 01-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 011-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 011.52 0C14.51 3.81 17 5 19 5a1 1 0 011 1z"}],["circle",{cx:"12",cy:"11",r:"2"}]],aC=[["path",{d:"M15 22a1 1 0 01-1-1v-4a1 1 0 01.445-.832l3-2a1 1 0 011.11 0l3 2A1 1 0 0122 17v4a1 1 0 01-1 1z"}],["path",{d:"M18 22v-3"}],["path",{d:"M20 10.418V6a1 1 0 00-1-1c-2 0-4.49-1.19-6.24-2.72a1.17 1.17 0 00-1.52 0C9.5 3.8 7 5 5 5a1 1 0 00-1 1v7c0 4.602 2.67 7.1 6.037 8.603"}]],hC=[["path",{d:"M20 9.807V6a1 1 0 00-1-1c-2 0-4.49-1.19-6.24-2.72a1.17 1.17 0 00-1.52 0C9.5 3.8 7 5 5 5a1 1 0 00-1 1v7c0 3.88 2.107 6.254 5 7.796"}],["path",{d:"M19 17v-2a2 2 0 00-4 0v2"}],["rect",{x:"13",y:"17",width:"8",height:"5",rx:"1"}]],tC=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9 12h6"}]],dC=[["path",{d:"m2 2 20 20"}],["path",{d:"M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71"}],["path",{d:"M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264"}]],cC=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9 12h6"}],["path",{d:"M12 9v6"}]],v0=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]],$0=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"m14.5 9.5-5 5"}],["path",{d:"m9.5 9.5 5 5"}]],MC=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M6.376 18.91a6 6 0 0 1 11.249.003"}],["circle",{cx:"12",cy:"11",r:"4"}]],pC=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}]],lC=[["path",{d:"M12 15v-3"}],["path",{d:"M12 2v2"}],["path",{d:"M16.5 12V9a1 1 0 011-1h1a1 1 0 001-1V5a1 1 0 00-1-1h-13a1 1 0 00-1 1v2a1 1 0 001 1h1a1 1 0 011 1v3"}],["path",{d:"M19.38 19c1.076-1.815 1.636-4.89 1.628-6.008a1 1 0 00-1-.992H3.984a1 1 0 00-1 .984c-.03 1.86.97 5.621 2.826 7.776"}],["path",{d:"M2 20c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1s1.2 1 2.5 1c2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}]],iC=[["circle",{cx:"12",cy:"12",r:"8"}],["path",{d:"M12 2v7.5"}],["path",{d:"m19 5-5.23 5.23"}],["path",{d:"M22 12h-7.5"}],["path",{d:"m19 19-5.23-5.23"}],["path",{d:"M12 14.5V22"}],["path",{d:"M10.23 13.77 5 19"}],["path",{d:"M9.5 12H2"}],["path",{d:"M10.23 10.23 5 5"}],["circle",{cx:"12",cy:"12",r:"2.5"}]],nC=[["path",{d:"M12 2v2"}],["path",{d:"M12 9.189V13"}],["path",{d:"M19 12V6a2 2 0 00-2-2H7a2 2 0 00-2 2v6"}],["path",{d:"M19.38 19A11.6 11.6 0 0021 13l-8.188-3.639a2 2 0 00-1.624 0L3 13.001a11.6 11.6 0 002.81 7.76"}],["path",{d:"M2 20c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1s1.2 1 2.5 1c2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}]],eC=[["path",{d:"M20.38 3.46 16 2a4 4 0 0 1-8 0L3.62 3.46a2 2 0 0 0-1.34 2.23l.58 3.47a1 1 0 0 0 .99.84H6v10c0 1.1.9 2 2 2h8a2 2 0 0 0 2-2V10h2.15a1 1 0 0 0 .99-.84l.58-3.47a2 2 0 0 0-1.34-2.23z"}]],rC=[["path",{d:"M16 10a4 4 0 0 1-8 0"}],["path",{d:"M3.103 6.034h17.794"}],["path",{d:"M3.4 5.467a2 2 0 0 0-.4 1.2V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.667a2 2 0 0 0-.4-1.2l-2-2.667A2 2 0 0 0 17 2H7a2 2 0 0 0-1.6.8z"}]],oC=[["path",{d:"M16 5h6"}],["path",{d:"m2.05 2.05 1.099-.028a1 1 0 011.008.815l2.69 14.347A1 1 0 007.83 18H18"}],["path",{d:"M4.564 5H12"}],["path",{d:"M6.25 14h12.712a2 2 0 001.991-1.57l.514-3.113"}],["circle",{cx:"18",cy:"20",r:"2"}],["circle",{cx:"8",cy:"20",r:"2"}]],vC=[["path",{d:"m15 11-1 9"}],["path",{d:"m19 11-4-7"}],["path",{d:"M2 11h20"}],["path",{d:"m3.5 11 1.6 7.4a2 2 0 0 0 2 1.6h9.8a2 2 0 0 0 2-1.6l1.7-7.4"}],["path",{d:"M4.5 15.5h15"}],["path",{d:"m5 11 4-7"}],["path",{d:"m9 11 1 9"}]],$C=[["path",{d:"M16 5h6"}],["path",{d:"M19 2v6"}],["path",{d:"m2.05 2.05 1.099-.028a1 1 0 011.008.815l2.69 14.347A1 1 0 007.83 18H18"}],["path",{d:"M4.564 5H12"}],["path",{d:"M6.25 14h12.712a2 2 0 001.991-1.57l.172-1.041"}],["circle",{cx:"18",cy:"20",r:"2"}],["circle",{cx:"8",cy:"20",r:"2"}]],mC=[["path",{d:"m2.05 2.05 1.099-.028a1 1 0 0 1 1.008.815l2.69 14.347A1 1 0 0 0 7.83 18H18"}],["path",{d:"M4.563 5h16.435a1 1 0 0 1 .981 1.204l-1.026 6.226A2 2 0 0 1 18.962 14H6.25"}],["circle",{cx:"18",cy:"20",r:"2"}],["circle",{cx:"8",cy:"20",r:"2"}]],yC=[["path",{d:"M21.56 4.56a1.5 1.5 0 0 1 0 2.122l-.47.47a3 3 0 0 1-4.212-.03 3 3 0 0 1 0-4.243l.44-.44a1.5 1.5 0 0 1 2.121 0z"}],["path",{d:"M3 22a1 1 0 0 1-1-1v-3.586a1 1 0 0 1 .293-.707l3.355-3.355a1.205 1.205 0 0 1 1.704 0l3.296 3.296a1.205 1.205 0 0 1 0 1.704l-3.355 3.355a1 1 0 0 1-.707.293z"}],["path",{d:"m9 15 7.879-7.878"}]],sC=[["path",{d:"m4 4 2.5 2.5"}],["path",{d:"M13.5 6.5a4.95 4.95 0 0 0-7 7"}],["path",{d:"M15 5 5 15"}],["path",{d:"M14 17v.01"}],["path",{d:"M10 16v.01"}],["path",{d:"M13 13v.01"}],["path",{d:"M16 10v.01"}],["path",{d:"M11 20v.01"}],["path",{d:"M17 14v.01"}],["path",{d:"M20 11v.01"}]],CC=[["path",{d:"M4 13V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v5"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 22v-5"}],["path",{d:"M14 19v-2"}],["path",{d:"M18 20v-3"}],["path",{d:"M2 13h20"}],["path",{d:"M6 20v-3"}]],uC=[["path",{d:"M10 2a3.28 3.28 0 003.227 1.798l6.17-.561A1 1 0 1119.614 8H13.5"}],["path",{d:"M11 20c-.5.5-1.12 1-2.5 1a1 1 0 010-5H12a7 7 0 003.283-.817"}],["path",{d:"M11 22c-.5-.5-1.12-1-2.5-1a6.5 6.5 0 01-5.63-3.25 6.44 6.44 0 015.236-9.744"}],["path",{d:"M18.04 12.54A7 7 0 0019 9V8"}],["path",{d:"m2 2 20 20"}],["path",{d:"M8 16c-2 0-4.5-4-4-6"}],["path",{d:"M9.43 9.33A8.5 8.5 0 0010 16"}]],gC=[["path",{d:"M10 2a3.28 3.28 0 003.227 1.798l6.17-.561A1 1 0 1119.614 8H8.5a6.44 6.44 0 00-5.63 9.75A6.5 6.5 0 008.5 21c1.38 0 2-.5 2.5-1"}],["path",{d:"M10 8a8.5 8.5 0 000 8"}],["path",{d:"M11 22c-.5-.5-1.12-1-2.5-1a1 1 0 010-5H12a7 7 0 007-7V8"}],["path",{d:"M13 12h.01"}],["path",{d:"M8 16c-2 0-4.5-4-4-6"}]],AC=[["path",{d:"m15 15 6 6m-6-6v4.8m0-4.8h4.8"}],["path",{d:"M9 19.8V15m0 0H4.2M9 15l-6 6"}],["path",{d:"M15 4.2V9m0 0h4.8M15 9l6-6"}],["path",{d:"M9 4.2V9m0 0H4.2M9 9 3 3"}]],HC=[["path",{d:"M12 22v-5.172a2 2 0 0 0-.586-1.414L9.5 13.5"}],["path",{d:"M14.5 14.5 12 17"}],["path",{d:"M17 8.8A6 6 0 0 1 13.8 20H10A6.5 6.5 0 0 1 7 8a5 5 0 0 1 10 0z"}]],VC=[["path",{d:"m18 14 4 4-4 4"}],["path",{d:"m18 2 4 4-4 4"}],["path",{d:"M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22"}],["path",{d:"M2 6h1.972a4 4 0 0 1 3.6 2.2"}],["path",{d:"M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45"}]],SC=[["path",{d:"M18 7V5a1 1 0 0 0-1-1H6.5a.5.5 0 0 0-.4.8l4.5 6a2 2 0 0 1 0 2.4l-4.5 6a.5.5 0 0 0 .4.8H17a1 1 0 0 0 1-1v-2"}]],wC=[["path",{d:"M2 20h.01"}],["path",{d:"M7 20v-4"}],["path",{d:"M12 20v-8"}],["path",{d:"M17 20V8"}]],LC=[["path",{d:"M2 20h.01"}],["path",{d:"M7 20v-4"}]],fC=[["path",{d:"M2 20h.01"}],["path",{d:"M7 20v-4"}],["path",{d:"M12 20v-8"}]],kC=[["path",{d:"M2 20h.01"}]],PC=[["path",{d:"M2 20h.01"}],["path",{d:"M7 20v-4"}],["path",{d:"M12 20v-8"}],["path",{d:"M17 20V8"}],["path",{d:"M22 4v16"}]],BC=[["path",{d:"m21 17-2.156-1.868A.5.5 0 0 0 18 15.5v.5a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1c0-2.545-3.991-3.97-8.5-4a1 1 0 0 0 0 5c4.153 0 4.745-11.295 5.708-13.5a2.5 2.5 0 1 1 3.31 3.284"}],["path",{d:"M3 21h18"}]],zC=[["path",{d:"M10 9H4L2 7l2-2h6"}],["path",{d:"M14 5h6l2 2-2 2h-6"}],["path",{d:"M10 22V4a2 2 0 1 1 4 0v18"}],["path",{d:"M8 22h8"}]],DC=[["path",{d:"M12 13v8"}],["path",{d:"M12 3v3"}],["path",{d:"M2.354 10.354a1.207 1.207 0 0 1 0-1.708l2.06-2.06A2 2 0 0 1 5.828 6h12.344a2 2 0 0 1 1.414.586l2.06 2.06a1.207 1.207 0 0 1 0 1.708l-2.06 2.06a2 2 0 0 1-1.414.586H5.828a2 2 0 0 1-1.414-.586z"}]],FC=[["path",{d:"M7 18v-6a5 5 0 1 1 10 0v6"}],["path",{d:"M5 21a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2z"}],["path",{d:"M21 12h1"}],["path",{d:"M18.5 4.5 18 5"}],["path",{d:"M2 12h1"}],["path",{d:"M12 2v1"}],["path",{d:"m4.929 4.929.707.707"}],["path",{d:"M12 12v6"}]],m0=[["path",{d:"M21 16V3a1 1 0 0 0-1.28-.96l-10 2.917a1 1 0 0 0-.72.96V19"}],["path",{d:"M21 6 9 9.5"}],["circle",{cx:"18",cy:"16",r:"3"}],["circle",{cx:"6",cy:"19",r:"3"}]],RC=[["path",{d:"M17.971 4.285A2 2 0 0 1 21 6v12a2 2 0 0 1-3.029 1.715l-9.997-5.998a2 2 0 0 1-.003-3.432z"}],["path",{d:"M3 20V4"}]],bC=[["path",{d:"M21 4v16"}],["path",{d:"M6.029 4.285A2 2 0 0 0 3 6v12a2 2 0 0 0 3.029 1.715l9.997-5.998a2 2 0 0 0 .003-3.432z"}]],TC=[["path",{d:"m12.5 17-.5-1-.5 1h1z"}],["path",{d:"M15 22a1 1 0 0 0 1-1v-1a2 2 0 0 0 1.56-3.25 8 8 0 1 0-11.12 0A2 2 0 0 0 8 20v1a1 1 0 0 0 1 1z"}],["circle",{cx:"15",cy:"12",r:"1"}],["circle",{cx:"9",cy:"12",r:"1"}]],qC=[["path",{d:"M22 2 2 22"}]],UC=[["path",{d:"M11 16.586V19a1 1 0 0 1-1 1H2L18.37 3.63a1 1 0 1 1 3 3l-9.663 9.663a1 1 0 0 1-1.414 0L8 14"}]],OC=[["path",{d:"M10 5H3"}],["path",{d:"M12 19H3"}],["path",{d:"M14 3v4"}],["path",{d:"M16 17v4"}],["path",{d:"M21 12h-9"}],["path",{d:"M21 19h-5"}],["path",{d:"M21 5h-7"}],["path",{d:"M8 10v4"}],["path",{d:"M8 12H3"}]],y0=[["path",{d:"M10 8h4"}],["path",{d:"M12 21v-9"}],["path",{d:"M12 8V3"}],["path",{d:"M17 16h4"}],["path",{d:"M19 12V3"}],["path",{d:"M19 21v-5"}],["path",{d:"M3 14h4"}],["path",{d:"M5 10V3"}],["path",{d:"M5 21v-7"}]],GC=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2"}],["path",{d:"M12.667 8 10 12h4l-2.667 4"}]],ZC=[["rect",{width:"7",height:"12",x:"2",y:"6",rx:"1"}],["path",{d:"M13 8.32a7.43 7.43 0 0 1 0 7.36"}],["path",{d:"M16.46 6.21a11.76 11.76 0 0 1 0 11.58"}],["path",{d:"M19.91 4.1a15.91 15.91 0 0 1 .01 15.8"}]],EC=[["path",{d:"M10 22H6a2 2 0 01-2-2V4a2 2 0 012-2h10a2 2 0 012 2v4"}],["path",{d:"M11 18h.01"}],["path",{d:"M22 22a4 4 0 00-8 0"}],["circle",{cx:"18",cy:"15",r:"3"}]],WC=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2"}],["path",{d:"M12 18h.01"}]],IC=[["path",{d:"M2 13a6 6 0 1 0 12 0 4 4 0 1 0-8 0 2 2 0 0 0 4 0"}],["circle",{cx:"10",cy:"13",r:"8"}],["path",{d:"M2 21h12c4.4 0 8-3.6 8-8V7a2 2 0 1 0-4 0v6"}],["path",{d:"M18 3 19.1 5.2"}],["path",{d:"M22 3 20.9 5.2"}]],XC=[["path",{d:"m10 20-1.25-2.5L6 18"}],["path",{d:"M10 4 8.75 6.5 6 6"}],["path",{d:"m14 20 1.25-2.5L18 18"}],["path",{d:"m14 4 1.25 2.5L18 6"}],["path",{d:"m17 21-3-6h-4"}],["path",{d:"m17 3-3 6 1.5 3"}],["path",{d:"M2 12h6.5L10 9"}],["path",{d:"m20 10-1.5 2 1.5 2"}],["path",{d:"M22 12h-6.5L14 15"}],["path",{d:"m4 10 1.5 2L4 14"}],["path",{d:"m7 21 3-6-1.5-3"}],["path",{d:"m7 3 3 6h4"}]],NC=[["path",{d:"M10.5 2v4"}],["path",{d:"M14 2H7a2 2 0 0 0-2 2"}],["path",{d:"M19.29 14.76A6.67 6.67 0 0 1 17 11a6.6 6.6 0 0 1-2.29 3.76c-1.15.92-1.71 2.04-1.71 3.19 0 2.22 1.8 4.05 4 4.05s4-1.83 4-4.05c0-1.16-.57-2.26-1.71-3.19"}],["path",{d:"M9.607 21H6a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h7V7a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3"}]],KC=[["path",{d:"M20 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v3"}],["path",{d:"M2 16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5a2 2 0 0 0-4 0v1.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V11a2 2 0 0 0-4 0z"}],["path",{d:"M4 18v2"}],["path",{d:"M20 18v2"}],["path",{d:"M12 4v9"}]],JC=[["path",{d:"M11 2h2"}],["path",{d:"m14.28 14-4.56 8"}],["path",{d:"m21 22-1.558-4H4.558"}],["path",{d:"M3 10v2"}],["path",{d:"M6.245 15.04A2 2 0 0 1 8 14h12a1 1 0 0 1 .864 1.505l-3.11 5.457A2 2 0 0 1 16 22H4a1 1 0 0 1-.863-1.506z"}],["path",{d:"M7 2a4 4 0 0 1-4 4"}],["path",{d:"m8.66 7.66 1.41 1.41"}]],QC=[["path",{d:"M11.248 3c.273.1.808.53.747 1.36-.05.83-.938 1.2-.989 2.02-.06.78.333 1.24.727 1.62"}],["path",{d:"M16.252 3c.268.1.794.53.745 1.36-.06.83-.923 1.2-.993 2.02-.05.78.338 1.24.725 1.62"}],["path",{d:"M19.5 12 22 6"}],["path",{d:"M4 12a1 1 0 00-.99 1.133A9 9 0 0012 21a9 9 0 008.99-7.867A1 1 0 0020 12z"}],["path",{d:"M6.252 3c.268.1.794.53.745 1.36-.06.83-.923 1.2-.993 2.02-.05.78.338 1.24.735 1.62"}],["path",{d:"M7 21h10"}]],jC=[["path",{d:"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1"}]],YC=[["path",{d:"M12 18v4"}],["path",{d:"M2 14.499a5.5 5.5 0 0 0 9.591 3.675.6.6 0 0 1 .818.001A5.5 5.5 0 0 0 22 14.5c0-2.29-1.5-4-3-5.5l-5.492-5.312a2 2 0 0 0-3-.02L5 8.999c-1.5 1.5-3 3.2-3 5.5"}]],_C=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"}]],s0=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"}],["path",{d:"M20 2v4"}],["path",{d:"M22 4h-4"}],["circle",{cx:"4",cy:"20",r:"2"}]],xC=[["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2"}],["path",{d:"M12 6h.01"}],["circle",{cx:"12",cy:"14",r:"4"}],["path",{d:"M12 14h.01"}]],au=[["path",{d:"m6 16 6-12 6 12"}],["path",{d:"M8 12h8"}],["path",{d:"M4 21c1.1 0 1.1-1 2.3-1s1.1 1 2.3 1c1.1 0 1.1-1 2.3-1 1.1 0 1.1 1 2.3 1 1.1 0 1.1-1 2.3-1 1.1 0 1.1 1 2.3 1 1.1 0 1.1-1 2.3-1"}]],hu=[["path",{d:"M8.8 20v-4.1l1.9.2a2.3 2.3 0 0 0 2.164-2.1V8.3A5.37 5.37 0 0 0 2 8.25c0 2.8.656 3.054 1 4.55a5.77 5.77 0 0 1 .029 2.758L2 20"}],["path",{d:"M19.8 17.8a7.5 7.5 0 0 0 .003-10.603"}],["path",{d:"M17 15a3.5 3.5 0 0 0-.025-4.975"}]],tu=[["path",{d:"m20 15-5.5 5.5L12 18"}],["path",{d:"m4 16 6-12 5.115 10.23"}],["path",{d:"M6 12h8"}]],du=[["path",{d:"M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"}],["path",{d:"M5 17A12 12 0 0 1 17 5"}],["circle",{cx:"19",cy:"5",r:"2"}],["circle",{cx:"5",cy:"19",r:"2"}]],cu=[["circle",{cx:"19",cy:"5",r:"2"}],["circle",{cx:"5",cy:"19",r:"2"}],["path",{d:"M5 17A12 12 0 0 1 17 5"}]],Mu=[["path",{d:"M16 3h5v5"}],["path",{d:"M8 3H3v5"}],["path",{d:"M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3"}],["path",{d:"m15 9 6-6"}]],pu=[["path",{d:"M17 13.44 4.442 17.082A2 2 0 0 0 4.982 21H19a2 2 0 0 0 .558-3.921l-1.115-.32A2 2 0 0 1 17 14.837V7.66"}],["path",{d:"m7 10.56 12.558-3.642A2 2 0 0 0 19.018 3H5a2 2 0 0 0-.558 3.921l1.115.32A2 2 0 0 1 7 9.163v7.178"}]],lu=[["path",{d:"m15 10.42 4.8-5.07"}],["path",{d:"M19 18h3"}],["path",{d:"M9.5 22 21.414 9.415A2 2 0 0 0 21.2 6.4l-5.61-4.208A1 1 0 0 0 14 3v2a2 2 0 0 1-1.394 1.906L8.677 8.053A1 1 0 0 0 8 9c-.155 6.393-2.082 9-4 9a2 2 0 0 0 0 4h14"}]],iu=[["path",{d:"M15.295 19.562 16 22"}],["path",{d:"m17 16 3.758 2.098"}],["path",{d:"m19 12.5 3.026-.598"}],["path",{d:"M7.61 6.3a3 3 0 0 0-3.92 1.3l-1.38 2.79a3 3 0 0 0 1.3 3.91l6.89 3.597a1 1 0 0 0 1.342-.447l3.106-6.211a1 1 0 0 0-.447-1.341z"}],["path",{d:"M8 9V2"}]],nu=[["path",{d:"M3 3h.01"}],["path",{d:"M7 5h.01"}],["path",{d:"M11 7h.01"}],["path",{d:"M3 7h.01"}],["path",{d:"M7 9h.01"}],["path",{d:"M3 11h.01"}],["rect",{width:"4",height:"4",x:"15",y:"5"}],["path",{d:"m19 9 2 2v10c0 .6-.4 1-1 1h-6c-.6 0-1-.4-1-1V11l2-2"}],["path",{d:"m13 14 8-2"}],["path",{d:"m13 19 8-2"}]],eu=[["path",{d:"M14 9.536V7a4 4 0 0 1 4-4h1.5a.5.5 0 0 1 .5.5V5a4 4 0 0 1-4 4 4 4 0 0 0-4 4c0 2 1 3 1 5a5 5 0 0 1-1 3"}],["path",{d:"M4 9a5 5 0 0 1 8 4 5 5 0 0 1-8-4"}],["path",{d:"M5 21h14"}]],C0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M17 12h-2l-2 5-2-10-2 5H7"}]],u0=[["path",{d:"M15 15H9l6-6"}],["path",{d:"M9 15V9"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],g0=[["path",{d:"M15 15 9 9"}],["path",{d:"M9 15h6V9"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],A0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 8v8"}],["path",{d:"m8 12 4 4 4-4"}]],H0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m12 8-4 4 4 4"}],["path",{d:"M16 12H8"}]],V0=[["path",{d:"M13 21h6a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6"}],["path",{d:"m3 21 9-9"}],["path",{d:"M9 21H3v-6"}]],S0=[["path",{d:"M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"}],["path",{d:"m21 21-9-9"}],["path",{d:"M21 15v6h-6"}]],w0=[["path",{d:"M13 3h6a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6"}],["path",{d:"m3 3 9 9"}],["path",{d:"M3 9V3h6"}]],L0=[["path",{d:"M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"}],["path",{d:"m21 3-9 9"}],["path",{d:"M15 3h6v6"}]],ru=[["path",{d:"m10 16 4-4-4-4"}],["path",{d:"M3 12h11"}],["path",{d:"M3 8V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-3"}]],ou=[["path",{d:"M10 12h11"}],["path",{d:"m17 16 4-4-4-4"}],["path",{d:"M21 6.344V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-1.344"}]],f0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M8 12h8"}],["path",{d:"m12 16 4-4-4-4"}]],k0=[["path",{d:"M15 15 9 9"}],["path",{d:"M9 15V9h6"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],P0=[["path",{d:"M15 15V9H9"}],["path",{d:"m9 15 6-6"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],B0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m16 12-4-4-4 4"}],["path",{d:"M12 16V8"}]],z0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 8v8"}],["path",{d:"m8.5 14 7-4"}],["path",{d:"m8.5 10 7 4"}]],D0=[["path",{d:"M14 21h1"}],["path",{d:"m17 17-2.18-2.18"}],["path",{d:"M5 21a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v14a2 2 0 01-2 2"}],["path",{d:"M9 21h1"}],["path",{d:"M9.56 14.44 17 7"}],["path",{d:"M9.56 9.56 12 12"}],["circle",{cx:"8.5",cy:"15.5",r:"1.5"}],["circle",{cx:"8.5",cy:"8.5",r:"1.5"}]],F0=[["path",{d:"M8 3H5a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h3"}],["path",{d:"M16 3h3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-3"}],["path",{d:"M12 20v2"}],["path",{d:"M12 14v2"}],["path",{d:"M12 8v2"}],["path",{d:"M12 2v2"}]],R0=[["path",{d:"M21 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v3"}],["path",{d:"M21 16v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-3"}],["path",{d:"M4 12H2"}],["path",{d:"M10 12H8"}],["path",{d:"M16 12h-2"}],["path",{d:"M22 12h-2"}]],m=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 8h7"}],["path",{d:"M8 12h6"}],["path",{d:"M11 16h5"}]],b0=[["path",{d:"M11 3v7.751a.25.25 0 00.407.195l2.28-1.834a.5.5 0 01.627 0l2.28 1.834a.25.25 0 00.406-.195V3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],T0=[["path",{d:"M21 10.656V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h12.344"}],["path",{d:"m9 11 3 3L22 4"}]],q0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m16 9-5.5 5.5L8 12"}]],U0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m10 8 4 4-4 4"}]],O0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m16 10-4 4-4-4"}]],G0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m8 14 4-4 4 4"}]],Z0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m14 16-4-4 4-4"}]],E0=[["path",{d:"m10 9-3 3 3 3"}],["path",{d:"m14 15 3-3-3-3"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],vu=[["path",{d:"M10 9.5 8 12l2 2.5"}],["path",{d:"M14 21h1"}],["path",{d:"m14 9.5 2 2.5-2 2.5"}],["path",{d:"M5 21a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2"}],["path",{d:"M9 21h1"}]],$u=[["path",{d:"M5 21a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2"}],["path",{d:"M9 21h1"}],["path",{d:"M14 21h1"}]],W0=[["path",{d:"M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"}],["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M9 3h1"}],["path",{d:"M9 21h2"}],["path",{d:"M14 3h1"}],["path",{d:"M3 9v1"}],["path",{d:"M21 9v2"}],["path",{d:"M3 14v1"}]],I0=[["path",{d:"M8 7v7"}],["path",{d:"M12 7v4"}],["path",{d:"M16 7v9"}],["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M9 3h1"}],["path",{d:"M14 3h1"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M21 9v1"}],["path",{d:"M21 14v1"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M14 21h1"}],["path",{d:"M9 21h1"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M3 14v1"}],["path",{d:"M3 9v1"}]],mu=[["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M9 3h1"}],["path",{d:"M9 21h1"}],["path",{d:"M14 3h1"}],["path",{d:"M14 21h1"}],["path",{d:"M3 9v1"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M21 14v1"}],["path",{d:"M8 12h8"}],["path",{d:"M12 8v8"}]],y=[["path",{d:"M14 21h1"}],["path",{d:"M14 3h1"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M21 14v1"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M3 9v1"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M7 12h10"}],["path",{d:"M7 16h6"}],["path",{d:"M7 8h8"}],["path",{d:"M9 21h1"}],["path",{d:"M9 3h1"}]],yu=[["path",{d:"M14 21h1"}],["path",{d:"M21 14v1"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2"}],["path",{d:"M3 9v1"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M9 21h1"}]],su=[["path",{d:"M14 3h1"}],["path",{d:"m16 16 5 5"}],["path",{d:"M19 3a2 2 0 012 2"}],["path",{d:"m21 16-5 5"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M3 9v1"}],["path",{d:"M5 21a2 2 0 01-2-2"}],["path",{d:"M5 3a2 2 0 00-2 2"}],["path",{d:"M9 21h1"}],["path",{d:"M9 3h1"}]],Cu=[["path",{d:"M14 21h1"}],["path",{d:"M14 3h1"}],["path",{d:"M19 3a2 2 0 012 2"}],["path",{d:"M21 14v1"}],["path",{d:"M21 19a2 2 0 01-2 2"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M3 9v1"}],["path",{d:"M5 21a2 2 0 01-2-2"}],["path",{d:"M5 3a2 2 0 00-2 2"}],["path",{d:"m9 15 6-6"}],["path",{d:"M9 21h1"}],["path",{d:"M9 3h1"}],["path",{d:"m9 9 6 6"}]],X0=[["path",{d:"M5 3a2 2 0 0 0-2 2"}],["path",{d:"M19 3a2 2 0 0 1 2 2"}],["path",{d:"M21 19a2 2 0 0 1-2 2"}],["path",{d:"M5 21a2 2 0 0 1-2-2"}],["path",{d:"M9 3h1"}],["path",{d:"M9 21h1"}],["path",{d:"M14 3h1"}],["path",{d:"M14 21h1"}],["path",{d:"M3 9v1"}],["path",{d:"M21 9v1"}],["path",{d:"M3 14v1"}],["path",{d:"M21 14v1"}]],uu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 7H7v5"}],["path",{d:"M12 17h5v-5"}]],N0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12"}],["line",{x1:"12",x2:"12",y1:"16",y2:"16"}],["line",{x1:"12",x2:"12",y1:"8",y2:"8"}]],K0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 10h10"}],["path",{d:"M7 14h10"}]],gu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]],J0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["circle",{cx:"12",cy:"12",r:"1"}]],Q0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"M9 17c2 0 2.8-1 2.8-2.8V10c0-2 1-3.3 3.2-3"}],["path",{d:"M9 11.2h5.7"}]],j0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M8 7v7"}],["path",{d:"M12 7v4"}],["path",{d:"M16 7v9"}]],Y0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 7v10"}],["path",{d:"M11 7v10"}],["path",{d:"m15 7 2 10"}]],_0=[["path",{d:"M8 16V8.5a.5.5 0 0 1 .9-.3l2.7 3.599a.5.5 0 0 0 .8 0l2.7-3.6a.5.5 0 0 1 .9.3V16"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],x0=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 8h10"}],["path",{d:"M7 12h10"}],["path",{d:"M7 16h10"}]],aa=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M8 12h8"}]],ha=[["path",{d:"M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"}],["path",{d:"M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"}]],Au=[["path",{d:"M20.4 20.4a2 2 0 01-1.4.6H5a2 2 0 01-2-2V5a2 2 0 01.59-1.41"}],["path",{d:"M21 15.3V5a2 2 0 00-2-2H8.7"}],["path",{d:"M22 22 2 2"}]],ta=[["path",{d:"M3.6 3.6A2 2 0 0 1 5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-.59 1.41"}],["path",{d:"M3 8.7V19a2 2 0 0 0 2 2h10.3"}],["path",{d:"m2 2 20 20"}],["path",{d:"M13 13a3 3 0 1 0 0-6H9v2"}],["path",{d:"M9 17v-2.3"}]],da=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 17V7h4a3 3 0 0 1 0 6H9"}]],Hu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["line",{x1:"10",x2:"10",y1:"15",y2:"9"}],["line",{x1:"14",x2:"14",y1:"15",y2:"9"}]],l=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"}]],ca=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"m15 9-6 6"}],["path",{d:"M9 9h.01"}],["path",{d:"M15 15h.01"}]],Ma=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 7h10"}],["path",{d:"M10 7v10"}],["path",{d:"M16 17a2 2 0 0 1-2-2V7"}]],pa=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M12 12H9.5a2.5 2.5 0 0 1 0-5H17"}],["path",{d:"M12 7v10"}],["path",{d:"M16 7v10"}]],la=[["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["path",{d:"M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z"}]],ia=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M8 12h8"}],["path",{d:"M12 8v8"}]],Vu=[["path",{d:"M7 12h2l2 5 2-10h4"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],na=[["path",{d:"M12 7v4"}],["path",{d:"M7.998 9.003a5 5 0 1 0 8-.005"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],Su=[["path",{d:"M21 11a8 8 0 0 0-8-8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"}]],ea=[["path",{d:"m17 17-2.18-2.18"}],["path",{d:"M9.56 14.44 17 7"}],["path",{d:"M9.56 9.56 12 12"}],["circle",{cx:"8.5",cy:"15.5",r:"1.5"}],["circle",{cx:"8.5",cy:"8.5",r:"1.5"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],ra=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M16 8.9V7H8l4 5-4 5h8v-1.9"}]],wu=[["path",{d:"M11 15H7"}],["path",{d:"M15.41 2.49a.6.6 0 011.18 0l.63 3.334a1.2 1.2 0 00.956.955l3.334.631a.6.6 0 010 1.18l-3.334.63a1.2 1.2 0 00-.955.956l-.631 3.334a.6.6 0 01-1.18 0l-.63-3.334a1.2 1.2 0 00-.956-.955L10.49 8.59a.6.6 0 010-1.18l3.334-.63a1.2 1.2 0 00.955-.956z"}],["path",{d:"M21 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h6"}],["path",{d:"M9 13v4"}]],oa=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["line",{x1:"9",x2:"15",y1:"15",y2:"9"}]],va=[["path",{d:"M12 2v20"}],["path",{d:"M16 3h3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-3"}],["path",{d:"M8 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3"}]],$a=[["path",{d:"M2 12h20"}],["path",{d:"M21 16v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-3"}],["path",{d:"M3 8V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v3"}]],Lu=[["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}],["rect",{x:"8",y:"8",width:"8",height:"8",rx:"1"}]],fu=[["path",{d:"M4 10c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h4c1.1 0 2 .9 2 2"}],["path",{d:"M10 16c-1.1 0-2-.9-2-2v-4c0-1.1.9-2 2-2h4c1.1 0 2 .9 2 2"}],["rect",{width:"8",height:"8",x:"14",y:"14",rx:"2"}]],ku=[["path",{d:"M11.035 7.69a1 1 0 0 1 1.909.024l.737 1.452a1 1 0 0 0 .737.535l1.634.256a1 1 0 0 1 .588 1.806l-1.172 1.168a1 1 0 0 0-.282.866l.259 1.613a1 1 0 0 1-1.541 1.134l-1.465-.75a1 1 0 0 0-.912 0l-1.465.75a1 1 0 0 1-1.539-1.133l.258-1.613a1 1 0 0 0-.282-.866l-1.156-1.153a1 1 0 0 1 .572-1.822l1.633-.256a1 1 0 0 0 .737-.535z"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],Pu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["rect",{x:"9",y:"9",width:"6",height:"6",rx:"1"}]],Bu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M7 8h8"}],["path",{d:"M7 12h10"}],["path",{d:"M7 16h6"}]],ma=[["path",{d:"M18 21a6 6 0 0 0-12 0"}],["circle",{cx:"12",cy:"11",r:"4"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]],ya=[["path",{d:"m7 11 2-2-2-2"}],["path",{d:"M11 13h4"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}]],sa=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"M7 21v-2a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v2"}]],Ca=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]],zu=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]],Du=[["path",{d:"M16 12v2a2 2 0 0 1-2 2H9a1 1 0 0 0-1 1v3a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h0"}],["path",{d:"M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 1-1 1h-5a2 2 0 0 0-2 2v2"}]],Fu=[["path",{d:"M10 22a2 2 0 0 1-2-2"}],["path",{d:"M14 2a2 2 0 0 1 2 2"}],["path",{d:"M16 22h-2"}],["path",{d:"M2 10V8"}],["path",{d:"M2 4a2 2 0 0 1 2-2"}],["path",{d:"M20 8a2 2 0 0 1 2 2"}],["path",{d:"M22 14v2"}],["path",{d:"M22 20a2 2 0 0 1-2 2"}],["path",{d:"M4 16a2 2 0 0 1-2-2"}],["path",{d:"M8 10a2 2 0 0 1 2-2h5a1 1 0 0 1 1 1v5a2 2 0 0 1-2 2H9a1 1 0 0 1-1-1z"}],["path",{d:"M8 2h2"}]],Ru=[["path",{d:"M10 22a2 2 0 0 1-2-2"}],["path",{d:"M16 22h-2"}],["path",{d:"M16 4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-5a2 2 0 0 1 2-2h5a1 1 0 0 0 1-1z"}],["path",{d:"M20 8a2 2 0 0 1 2 2"}],["path",{d:"M22 14v2"}],["path",{d:"M22 20a2 2 0 0 1-2 2"}]],bu=[["path",{d:"M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 0 1 1h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-3a1 1 0 0 0-1-1z"}]],Tu=[["path",{d:"M13.77 3.043a34 34 0 0 0-3.54 0"}],["path",{d:"M13.771 20.956a33 33 0 0 1-3.541.001"}],["path",{d:"M20.18 17.74c-.51 1.15-1.29 1.93-2.439 2.44"}],["path",{d:"M20.18 6.259c-.51-1.148-1.291-1.929-2.44-2.438"}],["path",{d:"M20.957 10.23a33 33 0 0 1 0 3.54"}],["path",{d:"M3.043 10.23a34 34 0 0 0 .001 3.541"}],["path",{d:"M6.26 20.179c-1.15-.508-1.93-1.29-2.44-2.438"}],["path",{d:"M6.26 3.82c-1.149.51-1.93 1.291-2.44 2.44"}]],qu=[["path",{d:"M15.236 22a3 3 0 0 0-2.2-5"}],["path",{d:"M16 20a3 3 0 0 1 3-3h1a2 2 0 0 0 2-2v-2a4 4 0 0 0-4-4V4"}],["path",{d:"M18 13h.01"}],["path",{d:"M18 6a4 4 0 0 0-4 4 7 7 0 0 0-7 7c0-5 4-5 4-10.5a4.5 4.5 0 1 0-9 0 2.5 2.5 0 0 0 5 0C7 10 3 11 3 17c0 2.8 2.2 5 5 5h10"}]],Uu=[["path",{d:"M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9-9-1.8-9-9 1.8-9 9-9"}]],Ou=[["path",{d:"M14 13V8.5C14 7 15 7 15 5a3 3 0 0 0-6 0c0 2 1 2 1 3.5V13"}],["path",{d:"M20 15.5a2.5 2.5 0 0 0-2.5-2.5h-11A2.5 2.5 0 0 0 4 15.5V17a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1z"}],["path",{d:"M5 22h14"}]],Gu=[["path",{d:"m19.06 12.501 2.78-2.707a.53.53 0 0 0-.294-.905l-5.166-.755a2.1 2.1 0 0 1-1.595-1.16l-2.31-4.68a.53.53 0 0 0-.95.001L9.216 6.974a2.1 2.1 0 0 1-1.597 1.16l-5.165.755a.53.53 0 0 0-.294.906l3.736 3.637a2.1 2.1 0 0 1 .611 1.879l-.88 5.139a.53.53 0 0 0 .769.56l4.617-2.428.027-.014"}],["path",{d:"m15 18 2 2 4-4"}]],Zu=[["path",{d:"M12 18.338a2.1 2.1 0 0 0-.987.244L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16l2.309-4.679A.53.53 0 0 1 12 2"}]],Eu=[["path",{d:"M15 18h6"}],["path",{d:"M17.688 14a2.1 2.1 0 0 1 .416-.568l3.736-3.638a.53.53 0 0 0-.294-.905l-5.166-.755a2.1 2.1 0 0 1-1.595-1.16l-2.31-4.68a.53.53 0 0 0-.95.001L9.216 6.974a2.1 2.1 0 0 1-1.597 1.16l-5.165.755a.53.53 0 0 0-.294.906l3.736 3.637a2.1 2.1 0 0 1 .611 1.879l-.88 5.139a.53.53 0 0 0 .769.56l4.617-2.428.027-.014"}]],Wu=[["path",{d:"m10.344 4.688 1.181-2.393a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.237 3.152"}],["path",{d:"m17.945 17.945.43 2.505a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a8 8 0 0 0 .4-.099"}],["path",{d:"m2 2 20 20"}]],Iu=[["path",{d:"M11.013 18.582 6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16l2.309-4.679a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904L20 11.5"}],["path",{d:"M15 18h6"}],["path",{d:"M18 15v6"}]],Xu=[["path",{d:"m15.5 15.5 5 5"}],["path",{d:"m20.063 11.525 1.777-1.731a.53.53 0 0 0-.294-.905l-5.166-.755a2.1 2.1 0 0 1-1.595-1.16l-2.31-4.68a.53.53 0 0 0-.95.001L9.216 6.974a2.1 2.1 0 0 1-1.597 1.16l-5.165.755a.53.53 0 0 0-.294.906l3.736 3.637a2.1 2.1 0 0 1 .611 1.879l-.88 5.139a.53.53 0 0 0 .769.56l4.617-2.428a2.1 2.1 0 0 1 .987-.243 2 2 0 0 1 .132.004"}],["path",{d:"m20.5 15.5-5 5"}]],Nu=[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"}]],Ku=[["path",{d:"M13.971 4.285A2 2 0 0 1 17 6v12a2 2 0 0 1-3.029 1.715l-9.997-5.998a2 2 0 0 1-.003-3.432z"}],["path",{d:"M21 20V4"}]],Ju=[["path",{d:"M10.029 4.285A2 2 0 0 0 7 6v12a2 2 0 0 0 3.029 1.715l9.997-5.998a2 2 0 0 0 .003-3.432z"}],["path",{d:"M3 4v16"}]],Qu=[["path",{d:"M11 2v2"}],["path",{d:"M5 2v2"}],["path",{d:"M5 3H4a2 2 0 0 0-2 2v4a6 6 0 0 0 12 0V5a2 2 0 0 0-2-2h-1"}],["path",{d:"M8 15a6 6 0 0 0 12 0v-3"}],["circle",{cx:"20",cy:"10",r:"2"}]],ju=[["path",{d:"M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z"}],["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 13h.01"}],["path",{d:"M16 13h.01"}],["path",{d:"M10 16s.8 1 2 1c1.3 0 2-1 2-1"}]],Yu=[["path",{d:"m15 19 2 2 4-4"}],["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"M21 13V9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6.5"}]],_u=[["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"M21 14V9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7.35"}],["path",{d:"M21 18h-6"}]],xu=[["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"m2 2 20 20"}],["path",{d:"M3.586 3.586A2 2 0 0 0 3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 1.414-.586"}],["path",{d:"M8.656 3H15a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 21 9v6.344"}]],ag=[["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"M18 15v6"}],["path",{d:"M21 12.356V9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7.355"}],["path",{d:"M21 18h-6"}]],hg=[["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}],["path",{d:"m16 16 5 5"}],["path",{d:"M21 12V9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7"}],["path",{d:"m21 16-5 5"}]],tg=[["path",{d:"M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z"}],["path",{d:"M15 3v5a1 1 0 0 0 1 1h5"}]],dg=[["path",{d:"M10 8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 16 14v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2z"}],["path",{d:"M10 8v5a1 1 0 0 0 1 1h5"}],["path",{d:"M8 4a2 2 0 0 1 2-2h6a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 22 8v6a2 2 0 0 1-2 2"}],["path",{d:"M16 2v5a1 1 0 0 0 1 1h5"}]],cg=[["path",{d:"M11.264 2.205A4 4 0 0 0 6.42 4.211l-4 8a4 4 0 0 0 1.359 5.117l6 4a4 4 0 0 0 4.438 0l6-4a4 4 0 0 0 1.576-4.592l-2-6a4 4 0 0 0-2.53-2.53z"}],["path",{d:"M11.99 22 14 12l7.822 3.184"}],["path",{d:"M14 12 8.47 2.302"}]],Mg=[["path",{d:"M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5"}],["path",{d:"M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244"}],["path",{d:"M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05"}]],pg=[["rect",{width:"20",height:"6",x:"2",y:"4",rx:"2"}],["rect",{width:"20",height:"6",x:"2",y:"14",rx:"2"}]],lg=[["rect",{width:"6",height:"20",x:"4",y:"2",rx:"2"}],["rect",{width:"6",height:"20",x:"14",y:"2",rx:"2"}]],ig=[["path",{d:"M16 4H9a3 3 0 0 0-2.83 4"}],["path",{d:"M14 12a4 4 0 0 1 0 8H6"}],["line",{x1:"4",x2:"20",y1:"12",y2:"12"}]],ng=[["path",{d:"m4 5 8 8"}],["path",{d:"m12 5-8 8"}],["path",{d:"M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"}]],eg=[["path",{d:"M15 4H7"}],["path",{d:"m18 16 3 3-3 3"}],["path",{d:"M3 4v13a2 2 0 0 0 2 2h16"}],["path",{d:"M7 14h7"}],["path",{d:"M7 9h12"}]],rg=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 4h.01"}],["path",{d:"M20 12h.01"}],["path",{d:"M12 20h.01"}],["path",{d:"M4 12h.01"}],["path",{d:"M17.657 6.343h.01"}],["path",{d:"M17.657 17.657h.01"}],["path",{d:"M6.343 17.657h.01"}],["path",{d:"M6.343 6.343h.01"}]],og=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 3v1"}],["path",{d:"M12 20v1"}],["path",{d:"M3 12h1"}],["path",{d:"M20 12h1"}],["path",{d:"m18.364 5.636-.707.707"}],["path",{d:"m6.343 17.657-.707.707"}],["path",{d:"m5.636 5.636.707.707"}],["path",{d:"m17.657 17.657.707.707"}]],vg=[["path",{d:"M12 2v2"}],["path",{d:"M14.837 16.385a6 6 0 1 1-7.223-7.222c.624-.147.97.66.715 1.248a4 4 0 0 0 5.26 5.259c.589-.255 1.396.09 1.248.715"}],["path",{d:"M16 12a4 4 0 0 0-4-4"}],["path",{d:"m19 5-1.256 1.256"}],["path",{d:"M20 12h2"}]],$g=[["path",{d:"M10 21v-1"}],["path",{d:"M10 4V3"}],["path",{d:"M10 9a3 3 0 0 0 0 6"}],["path",{d:"m14 20 1.25-2.5L18 18"}],["path",{d:"m14 4 1.25 2.5L18 6"}],["path",{d:"m17 21-3-6 1.5-3H22"}],["path",{d:"m17 3-3 6 1.5 3"}],["path",{d:"M2 12h1"}],["path",{d:"m20 10-1.5 2 1.5 2"}],["path",{d:"m3.64 18.36.7-.7"}],["path",{d:"m4.34 6.34-.7-.7"}]],mg=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 2v2"}],["path",{d:"M12 20v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"m17.66 17.66 1.41 1.41"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"m6.34 17.66-1.41 1.41"}],["path",{d:"m19.07 4.93-1.41 1.41"}]],yg=[["path",{d:"M12 2v8"}],["path",{d:"m4.93 10.93 1.41 1.41"}],["path",{d:"M2 18h2"}],["path",{d:"M20 18h2"}],["path",{d:"m19.07 10.93-1.41 1.41"}],["path",{d:"M22 22H2"}],["path",{d:"m8 6 4-4 4 4"}],["path",{d:"M16 18a4 4 0 0 0-8 0"}]],sg=[["path",{d:"M12 10V2"}],["path",{d:"m4.93 10.93 1.41 1.41"}],["path",{d:"M2 18h2"}],["path",{d:"M20 18h2"}],["path",{d:"m19.07 10.93-1.41 1.41"}],["path",{d:"M22 22H2"}],["path",{d:"m16 6-4 4-4-4"}],["path",{d:"M16 18a4 4 0 0 0-8 0"}]],Cg=[["path",{d:"m4 19 8-8"}],["path",{d:"m12 19-8-8"}],["path",{d:"M20 12h-4c0-1.5.442-2 1.5-2.5S20 8.334 20 7.002c0-.472-.17-.93-.484-1.29a2.105 2.105 0 0 0-2.617-.436c-.42.239-.738.614-.899 1.06"}]],ug=[["path",{d:"M11 17a4 4 0 0 1-8 0V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2Z"}],["path",{d:"M16.7 13H19a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H7"}],["path",{d:"M 7 17h.01"}],["path",{d:"m11 8 2.3-2.3a2.4 2.4 0 0 1 3.404.004L18.6 7.6a2.4 2.4 0 0 1 .026 3.434L9.9 19.8"}]],gg=[["path",{d:"M10 21V3h8"}],["path",{d:"M6 16h9"}],["path",{d:"M10 9.5h7"}]],Ag=[["path",{d:"M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"}],["path",{d:"M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"}],["circle",{cx:"12",cy:"12",r:"3"}],["path",{d:"m18 22-3-3 3-3"}],["path",{d:"m6 2 3 3-3 3"}]],Hg=[["path",{d:"m11 19-6-6"}],["path",{d:"m5 21-2-2"}],["path",{d:"m8 16-4 4"}],["path",{d:"M9.5 17.5 20.414 6.586A2 2 0 0021 5.172V3h-2.172a2 2 0 00-1.414.586L6.5 14.5"}]],Vg=[["path",{d:"m13 19 6-6"}],["path",{d:"M14.5 17.5 3.586 6.586A2 2 0 013 5.172V3h2.172a2 2 0 011.414.586L17.5 14.5"}],["path",{d:"m14.828 6.172 2.586-2.586A2 2 0 0118.828 3H21v2.172a2 2 0 01-.586 1.414l-2.586 2.586"}],["path",{d:"m16 16 4 4"}],["path",{d:"m19 21 2-2"}],["path",{d:"m5 14 4 4"}],["path",{d:"m5 21-2-2"}],["path",{d:"M7.5 16.5 4 20"}]],Sg=[["path",{d:"m18 2 4 4"}],["path",{d:"m17 7 3-3"}],["path",{d:"M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"}],["path",{d:"m9 11 4 4"}],["path",{d:"m5 19-3 3"}],["path",{d:"m14 4 6 6"}]],wg=[["path",{d:"M3 9h18"}],["path",{d:"M9 3v18"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],Lg=[["path",{d:"M12 21v-6"}],["path",{d:"M12 9V3"}],["path",{d:"M3 15h18"}],["path",{d:"M3 9h18"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]],fg=[["path",{d:"M12 15V9"}],["path",{d:"M3 15h18"}],["path",{d:"M3 9h18"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]],kg=[["path",{d:"M14 14v2"}],["path",{d:"M14 20v2"}],["path",{d:"M14 2v2"}],["path",{d:"M14 8v2"}],["path",{d:"M2 15h8"}],["path",{d:"M2 3h6a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2"}],["path",{d:"M2 9h8"}],["path",{d:"M22 15h-4"}],["path",{d:"M22 3h-2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h2"}],["path",{d:"M22 9h-4"}],["path",{d:"M5 3v18"}]],Pg=[["path",{d:"M16 5H3"}],["path",{d:"M16 12H3"}],["path",{d:"M16 19H3"}],["path",{d:"M21 5h.01"}],["path",{d:"M21 12h.01"}],["path",{d:"M21 19h.01"}]],Bg=[["path",{d:"M15 3v18"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M21 9H3"}],["path",{d:"M21 15H3"}]],zg=[["path",{d:"M12 3v18"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M3 9h18"}],["path",{d:"M3 15h18"}]],Dg=[["path",{d:"M14 10h2"}],["path",{d:"M15 22v-8"}],["path",{d:"M15 2v4"}],["path",{d:"M2 10h2"}],["path",{d:"M20 10h2"}],["path",{d:"M3 19h18"}],["path",{d:"M3 22v-6a2 2 135 0 1 2-2h14a2 2 45 0 1 2 2v6"}],["path",{d:"M3 2v2a2 2 45 0 0 2 2h14a2 2 135 0 0 2-2V2"}],["path",{d:"M8 10h2"}],["path",{d:"M9 22v-8"}],["path",{d:"M9 2v4"}]],Fg=[["rect",{width:"10",height:"14",x:"3",y:"8",rx:"2"}],["path",{d:"M5 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2h-2.4"}],["path",{d:"M8 18h.01"}]],Rg=[["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2",ry:"2"}],["line",{x1:"12",x2:"12.01",y1:"18",y2:"18"}]],bg=[["path",{d:"M16 13h6"}],["path",{d:"m16.5 6.5-3.914-3.914A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l1.79-1.79"}],["path",{d:"M19 10v6"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor"}]],Tg=[["circle",{cx:"7",cy:"7",r:"5"}],["circle",{cx:"17",cy:"17",r:"5"}],["path",{d:"M12 17h10"}],["path",{d:"m3.46 10.54 7.08-7.08"}]],qg=[["path",{d:"m16.5 6.5-3.914-3.914A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.43 2.43 0 0 0 3.42 0l1.79-1.79"}],["path",{d:"m16.5 10.5 5 5"}],["path",{d:"m21.5 10.5-5 5"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor"}]],Ug=[["path",{d:"M13.172 2a2 2 0 0 1 1.414.586l6.71 6.71a2.4 2.4 0 0 1 0 3.408l-4.592 4.592a2.4 2.4 0 0 1-3.408 0l-6.71-6.71A2 2 0 0 1 6 9.172V3a1 1 0 0 1 1-1z"}],["path",{d:"M2 7v6.172a2 2 0 0 0 .586 1.414l6.71 6.71a2.4 2.4 0 0 0 3.191.193"}],["circle",{cx:"10.5",cy:"6.5",r:".5",fill:"currentColor"}]],Og=[["path",{d:"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor"}]],Gg=[["path",{d:"M4 4v16"}]],Zg=[["path",{d:"M4 4v16"}],["path",{d:"M9 4v16"}]],Eg=[["path",{d:"M4 4v16"}],["path",{d:"M9 4v16"}],["path",{d:"M14 4v16"}]],Wg=[["path",{d:"M4 4v16"}],["path",{d:"M9 4v16"}],["path",{d:"M14 4v16"}],["path",{d:"M19 4v16"}],["path",{d:"M22 6 2 18"}]],Ig=[["path",{d:"M4 4v16"}],["path",{d:"M9 4v16"}],["path",{d:"M14 4v16"}],["path",{d:"M19 4v16"}]],Xg=[["circle",{cx:"17",cy:"4",r:"2"}],["path",{d:"M15.59 5.41 5.41 15.59"}],["circle",{cx:"4",cy:"17",r:"2"}],["path",{d:"M12 22s-4-9-1.5-11.5S22 12 22 12"}]],Ng=[["circle",{cx:"12",cy:"12",r:"10"}],["circle",{cx:"12",cy:"12",r:"6"}],["circle",{cx:"12",cy:"12",r:"2"}]],Kg=[["path",{d:"m10.065 12.493-6.18 1.318a.934.934 0 0 1-1.108-.702l-.537-2.15a1.07 1.07 0 0 1 .691-1.265l13.504-4.44"}],["path",{d:"m13.56 11.747 4.332-.924"}],["path",{d:"m16 21-3.105-6.21"}],["path",{d:"M16.485 5.94a2 2 0 0 1 1.455-2.425l1.09-.272a1 1 0 0 1 1.212.727l1.515 6.06a1 1 0 0 1-.727 1.213l-1.09.272a2 2 0 0 1-2.425-1.455z"}],["path",{d:"m6.158 8.633 1.114 4.456"}],["path",{d:"m8 21 3.105-6.21"}],["circle",{cx:"12",cy:"13",r:"2"}]],Jg=[["circle",{cx:"4",cy:"4",r:"2"}],["path",{d:"m14 5 3-3 3 3"}],["path",{d:"m14 10 3-3 3 3"}],["path",{d:"M17 14V2"}],["path",{d:"M17 14H7l-5 8h20Z"}],["path",{d:"M8 14v8"}],["path",{d:"m9 14 5 8"}]],Qg=[["path",{d:"M12 19h8"}],["path",{d:"m4 17 6-6-6-6"}]],jg=[["path",{d:"M3.5 21 14 3"}],["path",{d:"M20.5 21 10 3"}],["path",{d:"M15.5 21 12 15l-3.5 6"}],["path",{d:"M2 21h20"}]],ua=[["path",{d:"M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01a2.83 2.83 0 0 1 0-4L17 3"}],["path",{d:"m16 2 6 6"}],["path",{d:"M12 16H4"}]],Yg=[["path",{d:"M14.5 2v17.5c0 1.4-1.1 2.5-2.5 2.5c-1.4 0-2.5-1.1-2.5-2.5V2"}],["path",{d:"M8.5 2h7"}],["path",{d:"M14.5 16h-5"}]],_g=[["path",{d:"M9 2v17.5A2.5 2.5 0 0 1 6.5 22A2.5 2.5 0 0 1 4 19.5V2"}],["path",{d:"M20 2v17.5a2.5 2.5 0 0 1-2.5 2.5a2.5 2.5 0 0 1-2.5-2.5V2"}],["path",{d:"M3 2h7"}],["path",{d:"M14 2h7"}],["path",{d:"M9 16H4"}],["path",{d:"M20 16h-5"}]],ga=[["path",{d:"M21 5H3"}],["path",{d:"M17 12H7"}],["path",{d:"M19 19H5"}]],Aa=[["path",{d:"M21 5H3"}],["path",{d:"M21 12H9"}],["path",{d:"M21 19H7"}]],xg=[["path",{d:"M3 5h18"}],["path",{d:"M3 12h18"}],["path",{d:"M7 19h10"}]],aA=[["path",{d:"M3 5h18"}],["path",{d:"M3 12h18"}],["path",{d:"M11 19h10"}]],hA=[["path",{d:"M3 5h18"}],["path",{d:"M3 12h18"}],["path",{d:"M3 19h10"}]],Ha=[["path",{d:"M3 5h18"}],["path",{d:"M3 12h18"}],["path",{d:"M3 19h18"}]],tA=[["path",{d:"M12 20h-1a2 2 0 0 1-2-2 2 2 0 0 1-2 2H6"}],["path",{d:"M13 8h7a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-7"}],["path",{d:"M5 16H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h1"}],["path",{d:"M6 4h1a2 2 0 0 1 2 2 2 2 0 0 1 2-2h1"}],["path",{d:"M9 6v12"}]],s=[["path",{d:"M21 5H3"}],["path",{d:"M15 12H3"}],["path",{d:"M17 19H3"}]],dA=[["path",{d:"M17 22h-1a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1"}],["path",{d:"M7 22h1a4 4 0 0 0 4-4"}],["path",{d:"M7 2h1a4 4 0 0 1 4 4"}]],Va=[["path",{d:"M15 5h6"}],["path",{d:"M15 12h6"}],["path",{d:"M3 19h18"}],["path",{d:"m3 12 3.553-7.724a.5.5 0 0 1 .894 0L11 12"}],["path",{d:"M3.92 10h6.16"}]],cA=[["path",{d:"M17 5H3"}],["path",{d:"M21 12H8"}],["path",{d:"M21 19H8"}],["path",{d:"M3 12v7"}]],MA=[["path",{d:"M21 5H3"}],["path",{d:"M10 12H3"}],["path",{d:"M10 19H3"}],["circle",{cx:"17",cy:"15",r:"3"}],["path",{d:"m21 19-1.9-1.9"}]],Sa=[["path",{d:"m16 16-3 3 3 3"}],["path",{d:"M3 12h14.5a1 1 0 0 1 0 7H13"}],["path",{d:"M3 19h6"}],["path",{d:"M3 5h18"}]],pA=[["path",{d:"M2 10s3-3 3-8"}],["path",{d:"M22 10s-3-3-3-8"}],["path",{d:"M10 2c0 4.4-3.6 8-8 8"}],["path",{d:"M14 2c0 4.4 3.6 8 8 8"}],["path",{d:"M2 10s2 2 2 5"}],["path",{d:"M22 10s-2 2-2 5"}],["path",{d:"M8 15h8"}],["path",{d:"M2 22v-1a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1"}],["path",{d:"M14 22v-1a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1"}]],lA=[["path",{d:"m10 20-1.25-2.5L6 18"}],["path",{d:"M10 4 8.75 6.5 6 6"}],["path",{d:"M10.585 15H10"}],["path",{d:"M2 12h6.5L10 9"}],["path",{d:"M20 14.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0z"}],["path",{d:"m4 10 1.5 2L4 14"}],["path",{d:"m7 21 3-6-1.5-3"}],["path",{d:"m7 3 3 6h2"}]],iA=[["path",{d:"M12 2v2"}],["path",{d:"M12 8a4 4 0 0 0-1.645 7.647"}],["path",{d:"M2 12h2"}],["path",{d:"M20 14.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0z"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"m6.34 17.66-1.41 1.41"}]],nA=[["path",{d:"M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z"}]],eA=[["path",{d:"M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"}],["path",{d:"M17 14V2"}]],rA=[["path",{d:"M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"}],["path",{d:"M7 10v12"}]],oA=[["path",{d:"M12 2v20"}],["path",{d:"m21 16-5 5"}],["path",{d:"m21 21-5-5"}],["path",{d:"M22 12H2"}],["path",{d:"M8 3 3 8"}],["path",{d:"M8 8 3 3"}],["circle",{cx:"18.5",cy:"5.5",r:"2.5"}],["circle",{cx:"5.5",cy:"18.5",r:"2.5"}]],vA=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"m9 12 2 2 4-4"}]],$A=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"M9 12h6"}]],mA=[["path",{d:"M2 9a3 3 0 1 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 1 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"M9 9h.01"}],["path",{d:"m15 9-6 6"}],["path",{d:"M15 15h.01"}]],yA=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"M9 12h6"}],["path",{d:"M12 9v6"}]],sA=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"m9.5 14.5 5-5"}]],CA=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"m9.5 14.5 5-5"}],["path",{d:"m9.5 9.5 5 5"}]],uA=[["path",{d:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"}],["path",{d:"M13 5v2"}],["path",{d:"M13 17v2"}],["path",{d:"M13 11v2"}]],gA=[["path",{d:"M10.5 17h1.227a2 2 0 0 0 1.345-.52L18 12"}],["path",{d:"m12 13.5 3.794.506"}],["path",{d:"m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8"}],["path",{d:"M6 10V8"}],["path",{d:"M6 14v1"}],["path",{d:"M6 19v2"}],["rect",{x:"2",y:"8",width:"20",height:"13",rx:"2"}]],AA=[["path",{d:"M4 12h.01"}],["path",{d:"M4 16h.01"}],["path",{d:"M4 20h.01"}],["path",{d:"M4 4h.01"}],["path",{d:"M4 8h.01"}],["path",{d:"M9.414 13.414a2 2 0 0 0 1.414.586H19a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 12z"}],["path",{d:"M9.414 21.414a2 2 0 0 0 1.414.586H19a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 20z"}],["path",{d:"M9.414 5.414A2 2 0 0 0 10.828 6H19a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 4z"}]],HA=[["path",{d:"m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8"}],["path",{d:"M6 10V8"}],["path",{d:"M6 14v1"}],["path",{d:"M6 19v2"}],["rect",{x:"2",y:"8",width:"20",height:"13",rx:"2"}]],VA=[["path",{d:"M10 2h4"}],["path",{d:"M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7"}],["path",{d:"M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0 0 1 .9 10.2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M12 12v-2"}]],SA=[["path",{d:"M10 2h4"}],["path",{d:"M12 14v-4"}],["path",{d:"M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6"}],["path",{d:"M9 17H4v5"}]],wA=[["line",{x1:"10",x2:"14",y1:"2",y2:"2"}],["line",{x1:"12",x2:"15",y1:"14",y2:"11"}],["circle",{cx:"12",cy:"14",r:"8"}]],LA=[["circle",{cx:"9",cy:"12",r:"3"}],["rect",{width:"20",height:"14",x:"2",y:"5",rx:"7"}]],fA=[["circle",{cx:"15",cy:"12",r:"3"}],["rect",{width:"20",height:"14",x:"2",y:"5",rx:"7"}]],kA=[["path",{d:"M7 12h13a1 1 0 0 1 1 1 5 5 0 0 1-5 5h-.598a.5.5 0 0 0-.424.765l1.544 2.47a.5.5 0 0 1-.424.765H5.402a.5.5 0 0 1-.424-.765L7 18"}],["path",{d:"M8 18a5 5 0 0 1-5-5V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8"}]],PA=[["path",{d:"M11 10h2"}],["path",{d:"M19 21V10a7 7 0 00-14 0v11"}],["path",{d:"M2 21h20"}],["path",{d:"M9 14h6"}]],BA=[["path",{d:"M10 15h4"}],["path",{d:"m14.5 4.058.571-1.429a1 1 0 011.3-.558l5 2a1 1 0 01.549.534.88.88 0 01.01.765l-2.332 5.83"}],["path",{d:"M15.634 11a2 2 0 00-.186-.34l-1.17-1.757 1.31-1.656a2 2 0 00-2.109-3.165l-2.032.57-1.17-1.758a2 2 0 00-3.664 1.028l-.086 2.11-2.032.568a2 2 0 00-.155 3.8l1.617.6"}],["path",{d:"M4 12.006A1 1 0 014.994 11H19a1 1 0 011 1v7a2 2 0 01-2 2H6a2 2 0 01-2-2z"}]],zA=[["path",{d:"M16 12v4"}],["path",{d:"M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"}],["path",{d:"M17 6a2 2 0 011.414.586l3 3A2 2 0 0122 11v8a2 2 0 01-2 2H4a2 2 0 01-2-2v-8a2 2 0 01.586-1.414l3-3A2 2 0 017 6z"}],["path",{d:"M2 14h20"}],["path",{d:"M8 12v4"}]],DA=[["path",{d:"M10 3H8"}],["path",{d:"M14.586 11.414 22 4"}],["path",{d:"M15 11a8 8 0 01-.429.4"}],["path",{d:"m2 22 7-7c1.857-1.857 3.714-1.99 5.571-3.6l-1.985-1.986A2 2 0 0114 6a2 2 0 012-2 2 2 0 013.262-1.552l2.152 2.138"}],["path",{d:"M20 15v4"}],["path",{d:"M22 17h-4"}],["path",{d:"M4 5v4"}],["path",{d:"M6 7H2"}],["path",{d:"M9 2v2"}]],FA=[["path",{d:"M15 11c-2 2-4 2-6 4l-7 7"}],["path",{d:"m22 4-7.414 7.414-2-2A2 2 0 0114 6c0-.512.196-1.024.586-1.414A2 2 0 0116 4a2 2 0 013.262-1.552l2.152 2.138"}]],RA=[["path",{d:"M21 4H3"}],["path",{d:"M18 8H6"}],["path",{d:"M19 12H9"}],["path",{d:"M16 16h-6"}],["path",{d:"M11 20H9"}]],bA=[["ellipse",{cx:"12",cy:"11",rx:"3",ry:"2"}],["ellipse",{cx:"12",cy:"12.5",rx:"10",ry:"8.5"}]],TA=[["path",{d:"M12 20v-6"}],["path",{d:"M19.656 14H22"}],["path",{d:"M2 14h12"}],["path",{d:"m2 2 20 20"}],["path",{d:"M20 20H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2"}],["path",{d:"M9.656 4H20a2 2 0 0 1 2 2v10.344"}]],qA=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M2 14h20"}],["path",{d:"M12 20v-6"}]],UA=[["path",{d:"M18.2 12.27 20 6H4l1.8 6.27a1 1 0 0 0 .95.73h10.5a1 1 0 0 0 .96-.73Z"}],["path",{d:"M8 13v9"}],["path",{d:"M16 22v-9"}],["path",{d:"m9 6 1 7"}],["path",{d:"m15 6-1 7"}],["path",{d:"M12 6V2"}],["path",{d:"M13 2h-2"}]],OA=[["path",{d:"M22 7h-2"}],["path",{d:"M6.5 3h11A2.5 2.5 0 0 1 20 5.5V20a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1V5.5a1 1 0 0 0-5 0V17a1 1 0 0 0 1 1h4"}],["path",{d:"M9 7H2"}]],GA=[["rect",{width:"18",height:"12",x:"3",y:"8",rx:"1"}],["path",{d:"M10 8V5c0-.6-.4-1-1-1H6a1 1 0 0 0-1 1v3"}],["path",{d:"M19 8V5c0-.6-.4-1-1-1h-3a1 1 0 0 0-1 1v3"}]],ZA=[["path",{d:"m10 11 11 .9a1 1 0 0 1 .8 1.1l-.665 4.158a1 1 0 0 1-.988.842H20"}],["path",{d:"M16 18h-5"}],["path",{d:"M18 5a1 1 0 0 0-1 1v5.573"}],["path",{d:"M3 4h8.129a1 1 0 0 1 .99.863L13 11.246"}],["path",{d:"M4 11V4"}],["path",{d:"M7 15h.01"}],["path",{d:"M8 10.1V4"}],["circle",{cx:"18",cy:"18",r:"2"}],["circle",{cx:"7",cy:"15",r:"5"}]],EA=[["path",{d:"M16.05 10.966a5 2.5 0 0 1-8.1 0"}],["path",{d:"m16.923 14.049 4.48 2.04a1 1 0 0 1 .001 1.831l-8.574 3.9a2 2 0 0 1-1.66 0l-8.574-3.91a1 1 0 0 1 0-1.83l4.484-2.04"}],["path",{d:"M16.949 14.14a5 2.5 0 1 1-9.9 0L10.063 3.5a2 2 0 0 1 3.874 0z"}],["path",{d:"M9.194 6.57a5 2.5 0 0 0 5.61 0"}]],WA=[["path",{d:"M10 11.341V10"}],["path",{d:"M14 13v-3"}],["path",{d:"M18 17V8a2 2 0 00-2-2H4a2 2 0 00-2 2v7a2 2 0 002 2h2"}],["path",{d:"M22 15v1a1 1 0 01-1 1H10"}],["path",{d:"M6 11.341V10"}],["circle",{cx:"8",cy:"17",r:"2"}]],IA=[["path",{d:"M2 22V12a10 10 0 1 1 20 0v10"}],["path",{d:"M15 6.8v1.4a3 2.8 0 1 1-6 0V6.8"}],["path",{d:"M10 15h.01"}],["path",{d:"M14 15h.01"}],["path",{d:"M10 19a4 4 0 0 1-4-4v-3a6 6 0 1 1 12 0v3a4 4 0 0 1-4 4Z"}],["path",{d:"m9 19-2 3"}],["path",{d:"m15 19 2 3"}]],XA=[["path",{d:"M8 3.1V7a4 4 0 0 0 8 0V3.1"}],["path",{d:"m9 15-1-1"}],["path",{d:"m15 15 1-1"}],["path",{d:"M9 19c-2.8 0-5-2.2-5-5v-4a8 8 0 0 1 16 0v4c0 2.8-2.2 5-5 5Z"}],["path",{d:"m8 19-2 3"}],["path",{d:"m16 19 2 3"}]],NA=[["path",{d:"M2 17 17 2"}],["path",{d:"m2 14 8 8"}],["path",{d:"m5 11 8 8"}],["path",{d:"m8 8 8 8"}],["path",{d:"m11 5 8 8"}],["path",{d:"m14 2 8 8"}],["path",{d:"M7 22 22 7"}]],KA=[["path",{d:"M12 16v6"}],["path",{d:"M14 20h-4"}],["path",{d:"M18 2h4v4"}],["path",{d:"m2 2 7.17 7.17"}],["path",{d:"M2 5.355V2h3.357"}],["path",{d:"m22 2-7.17 7.17"}],["path",{d:"M8 5 5 8"}],["circle",{cx:"12",cy:"12",r:"4"}]],wa=[["rect",{width:"16",height:"16",x:"4",y:"3",rx:"2"}],["path",{d:"M4 11h16"}],["path",{d:"M12 3v8"}],["path",{d:"m8 19-2 3"}],["path",{d:"m18 22-2-3"}],["path",{d:"M8 15h.01"}],["path",{d:"M16 15h.01"}]],JA=[["path",{d:"M10 11v6"}],["path",{d:"M14 17v-3"}],["path",{d:"M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-1.576.768"}],["path",{d:"M19 6v7.344"}],["path",{d:"m2 2 20 20"}],["path",{d:"M21 6h-9.344"}],["path",{d:"M3 6h3"}],["path",{d:"M5 6v14a2 2 0 002 2h10a2 2 0 002-2v-1"}]],La=[["path",{d:"M10 11v6"}],["path",{d:"M14 11v6"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"}],["path",{d:"M3 6h18"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"}]],QA=[["path",{d:"M8 19a4 4 0 0 1-2.24-7.32A3.5 3.5 0 0 1 9 6.03V6a3 3 0 1 1 6 0v.04a3.5 3.5 0 0 1 3.24 5.65A4 4 0 0 1 16 19Z"}],["path",{d:"M12 19v3"}]],fa=[["path",{d:"M13 8c0-2.76-2.46-5-5.5-5S2 5.24 2 8h2l1-1 1 1h4"}],["path",{d:"M13 7.14A5.82 5.82 0 0 1 16.5 6c3.04 0 5.5 2.24 5.5 5h-3l-1-1-1 1h-3"}],["path",{d:"M5.89 9.71c-2.15 2.15-2.3 5.47-.35 7.43l4.24-4.25.7-.7.71-.71 2.12-2.12c-1.95-1.96-5.27-1.8-7.42.35"}],["path",{d:"M11 15.5c.5 2.5-.17 4.5-1 6.5h4c2-5.5-.5-12-1-14"}]],jA=[["path",{d:"m17 14 3 3.3a1 1 0 0 1-.7 1.7H4.7a1 1 0 0 1-.7-1.7L7 14h-.3a1 1 0 0 1-.7-1.7L9 9h-.2A1 1 0 0 1 8 7.3L12 3l4 4.3a1 1 0 0 1-.8 1.7H15l3 3.3a1 1 0 0 1-.7 1.7H17Z"}],["path",{d:"M12 22v-3"}]],YA=[["path",{d:"M10 10v.2A3 3 0 0 1 8.9 16H5a3 3 0 0 1-1-5.8V10a3 3 0 0 1 6 0Z"}],["path",{d:"M7 16v6"}],["path",{d:"M13 19v3"}],["path",{d:"M12 19h8.3a1 1 0 0 0 .7-1.7L18 14h.3a1 1 0 0 0 .7-1.7L16 9h.2a1 1 0 0 0 .8-1.7L13 3l-1.4 1.5"}]],_A=[["path",{d:"M16 17h6v-6"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7"}]],xA=[["path",{d:"M14.828 14.828 21 21"}],["path",{d:"M21 16v5h-5"}],["path",{d:"m21 3-9 9-4-4-6 6"}],["path",{d:"M21 8V3h-5"}]],aH=[["path",{d:"M16 7h6v6"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17"}]],ka=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"}],["path",{d:"M12 9v4"}],["path",{d:"M12 17h.01"}]],hH=[["path",{d:"M10.17 4.193a2 2 0 0 1 3.666.013"}],["path",{d:"M14 21h2"}],["path",{d:"m15.874 7.743 1 1.732"}],["path",{d:"m18.849 12.952 1 1.732"}],["path",{d:"M21.824 18.18a2 2 0 0 1-1.835 2.824"}],["path",{d:"M4.024 21a2 2 0 0 1-1.839-2.839"}],["path",{d:"m5.136 12.952-1 1.732"}],["path",{d:"M8 21h2"}],["path",{d:"m8.102 7.743-1 1.732"}]],tH=[["path",{d:"M22 18a2 2 0 0 1-2 2H3c-1.1 0-1.3-.6-.4-1.3L20.4 4.3c.9-.7 1.6-.4 1.6.7Z"}]],dH=[["path",{d:"M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"}]],Pa=[["path",{d:"M10 12H8"}],["path",{d:"M16 12h-2"}],["path",{d:"M22 12h-2"}],["path",{d:"M4 12H2"}],["path",{d:"M7.298 20.288A1 1 0 008 22h8a1 1 0 00.703-1.712l-3.991-3.99a1 1 0 00-1.424-.001z"}],["path",{d:"M7.298 3.712A1 1 0 018 2h8a1 1 0 01.703 1.712l-3.991 3.99a1 1 0 01-1.424.001z"}]],Ba=[["path",{d:"M12 14v2"}],["path",{d:"M12 20v2"}],["path",{d:"M12 2v2"}],["path",{d:"M12 8v2"}],["path",{d:"M20.288 16.703A1 1 0 0022 16V8a1 1 0 00-1.712-.703l-3.99 3.991a1 1 0 00-.001 1.424z"}],["path",{d:"M3.712 16.703A1 1 0 012 16V8a1 1 0 011.712-.703l3.99 3.991a1 1 0 01.001 1.424z"}]],cH=[["path",{d:"M10 14.66V17a1 1 0 0 1-1 1 2 2 0 0 0-2 2v2"}],["path",{d:"M14 14.66V17a1 1 0 0 0 1 1 2 2 0 0 1 2 2v2"}],["path",{d:"M17.916 10H19.5A2.5 2.5 0 0 0 22 7.5V5a1 1 0 0 0-1-1h-3"}],["path",{d:"M4 22h16"}],["path",{d:"M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z"}],["path",{d:"M6.084 10H4.5A2.5 2.5 0 0 1 2 7.5V5a1 1 0 0 1 1-1h3"}]],MH=[["path",{d:"M14 19V7a2 2 0 0 0-2-2H9"}],["path",{d:"M15 19H9"}],["path",{d:"M19 19h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62L18.3 9.38a1 1 0 0 0-.78-.38H14"}],["path",{d:"M2 13v5a1 1 0 0 0 1 1h2"}],["path",{d:"M4 3 2.15 5.15a.495.495 0 0 0 .35.86h2.15a.47.47 0 0 1 .35.86L3 9.02"}],["circle",{cx:"17",cy:"19",r:"2"}],["circle",{cx:"7",cy:"19",r:"2"}]],pH=[["path",{d:"M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2"}],["path",{d:"M15 18H9"}],["path",{d:"M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14"}],["circle",{cx:"17",cy:"18",r:"2"}],["circle",{cx:"7",cy:"18",r:"2"}]],lH=[["path",{d:"M15 18v3a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-3"}],["path",{d:"M17 2a2 2 0 0 1 1.6 3.2A8 8 0 0 0 17 10v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2v-6a8 8 0 0 0-1.6-4.8A2 2 0 0 1 7 2z"}],["path",{d:"M7 10a6.47 6.47 0 0 1 5 0 6.47 6.47 0 0 0 5 0"}]],iH=[["path",{d:"M15 4 5 9"}],["path",{d:"m15 8.5-10 5"}],["path",{d:"M18 12a9 9 0 0 1-9 9V3"}]],nH=[["path",{d:"M10 12.01h.01"}],["path",{d:"M18 8v4a8 8 0 0 1-1.07 4"}],["circle",{cx:"10",cy:"12",r:"4"}],["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}]],eH=[["path",{d:"m12 10 2 4v3a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-3a8 8 0 1 0-16 0v3a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-3l2-4h4Z"}],["path",{d:"M4.82 7.9 8 10"}],["path",{d:"M15.18 7.9 12 10"}],["path",{d:"M16.93 10H20a2 2 0 0 1 0 4H2"}]],rH=[["path",{d:"M15.033 9.44a.647.647 0 0 1 0 1.12l-4.065 2.352a.645.645 0 0 1-.968-.56V7.648a.645.645 0 0 1 .967-.56z"}],["path",{d:"M7 21h10"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}]],za=[["path",{d:"M7 21h10"}],["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}]],oH=[["path",{d:"m17 2-5 5-5-5"}],["rect",{width:"20",height:"15",x:"2",y:"7",rx:"2"}]],vH=[["path",{d:"M14 16.5a.5.5 0 0 0 .5.5h.5a2 2 0 0 1 0 4H9a2 2 0 0 1 0-4h.5a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5V8a2 2 0 0 1-4 0V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v3a2 2 0 0 1-4 0v-.5a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5Z"}]],$H=[["path",{d:"M12 13v7a2 2 0 0 0 4 0"}],["path",{d:"M12 2v2"}],["path",{d:"M18.656 13h2.336a1 1 0 0 0 .97-1.274 10.284 10.284 0 0 0-12.07-7.51"}],["path",{d:"m2 2 20 20"}],["path",{d:"M5.961 5.957a10.28 10.28 0 0 0-3.922 5.769A1 1 0 0 0 3 13h10"}]],mH=[["path",{d:"M12 13v7a2 2 0 0 0 4 0"}],["path",{d:"M12 2v2"}],["path",{d:"M20.992 13a1 1 0 0 0 .97-1.274 10.284 10.284 0 0 0-19.923 0A1 1 0 0 0 3 13z"}]],yH=[["path",{d:"M6 4v6a6 6 0 0 0 12 0V4"}],["line",{x1:"4",x2:"20",y1:"20",y2:"20"}]],sH=[["path",{d:"M12 4v16"}],["path",{d:"M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2"}],["path",{d:"M9 20h6"}]],CH=[["path",{d:"M9 14 4 9l5-5"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11"}]],uH=[["path",{d:"M21 17a9 9 0 0 0-15-6.7L3 13"}],["path",{d:"M3 7v6h6"}],["circle",{cx:"12",cy:"17",r:"1"}]],gH=[["path",{d:"M3 7v6h6"}],["path",{d:"M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"}]],AH=[["path",{d:"M16 12h6"}],["path",{d:"M8 12H2"}],["path",{d:"M12 2v2"}],["path",{d:"M12 8v2"}],["path",{d:"M12 14v2"}],["path",{d:"M12 20v2"}],["path",{d:"m19 15 3-3-3-3"}],["path",{d:"m5 9-3 3 3 3"}]],HH=[["path",{d:"M12 22v-6"}],["path",{d:"M12 8V2"}],["path",{d:"M4 12H2"}],["path",{d:"M10 12H8"}],["path",{d:"M16 12h-2"}],["path",{d:"M22 12h-2"}],["path",{d:"m15 19-3 3-3-3"}],["path",{d:"m15 5-3-3-3 3"}]],VH=[["rect",{x:"11",y:"14",width:"10",height:"7",rx:"2"}],["rect",{x:"3",y:"3",width:"10",height:"7",rx:"2"}]],Da=[["path",{d:"M14 21v-3a2 2 0 0 0-4 0v3"}],["path",{d:"M18 12h.01"}],["path",{d:"M18 16h.01"}],["path",{d:"M22 7a1 1 0 0 0-1-1h-2a2 2 0 0 1-1.143-.359L13.143 2.36a2 2 0 0 0-2.286-.001L6.143 5.64A2 2 0 0 1 5 6H3a1 1 0 0 0-1 1v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2z"}],["path",{d:"M6 12h.01"}],["path",{d:"M6 16h.01"}],["circle",{cx:"12",cy:"10",r:"2"}]],SH=[["path",{d:"M15 7h2a5 5 0 0 1 0 10h-2m-6 0H7A5 5 0 0 1 7 7h2"}]],wH=[["path",{d:"m18.84 12.25 1.72-1.71h-.02a5.004 5.004 0 0 0-.12-7.07 5.006 5.006 0 0 0-6.95 0l-1.72 1.71"}],["path",{d:"m5.17 11.75-1.71 1.71a5.004 5.004 0 0 0 .12 7.07 5.006 5.006 0 0 0 6.95 0l1.71-1.71"}],["line",{x1:"8",x2:"8",y1:"2",y2:"5"}],["line",{x1:"2",x2:"5",y1:"8",y2:"8"}],["line",{x1:"16",x2:"16",y1:"19",y2:"22"}],["line",{x1:"19",x2:"22",y1:"16",y2:"16"}]],LH=[["path",{d:"m19 5 3-3"}],["path",{d:"m2 22 3-3"}],["path",{d:"M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"}],["path",{d:"M7.5 13.5 10 11"}],["path",{d:"M10.5 16.5 13 14"}],["path",{d:"m12 6 6 6 2.3-2.3a2.4 2.4 0 0 0 0-3.4l-2.6-2.6a2.4 2.4 0 0 0-3.4 0Z"}]],fH=[["path",{d:"M12 3v12"}],["path",{d:"m17 8-5-5-5 5"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}]],kH=[["path",{d:"M6 12h12"}],["rect",{x:"2",y:"8",width:"20",height:"8",rx:"4"}]],PH=[["path",{d:"m16 11 2 2 4-4"}],["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["circle",{cx:"9",cy:"7",r:"4"}]],BH=[["path",{d:"M10 15H6a4 4 0 0 0-4 4v2"}],["path",{d:"m14.305 16.53.923-.382"}],["path",{d:"m15.228 13.852-.923-.383"}],["path",{d:"m16.852 12.228-.383-.923"}],["path",{d:"m16.852 17.772-.383.924"}],["path",{d:"m19.148 12.228.383-.923"}],["path",{d:"m19.53 18.696-.382-.924"}],["path",{d:"m20.772 13.852.924-.383"}],["path",{d:"m20.772 16.148.924.383"}],["circle",{cx:"18",cy:"15",r:"3"}],["circle",{cx:"9",cy:"7",r:"4"}]],zH=[["circle",{cx:"10",cy:"7",r:"1"}],["circle",{cx:"4",cy:"20",r:"1"}],["path",{d:"M4.7 19.3 19 5"}],["path",{d:"m21 3-3 1 2 2Z"}],["path",{d:"M9.26 7.68 5 12l2 5"}],["path",{d:"m10 14 5 2 3.5-3.5"}],["path",{d:"m18 12 1-1 1 1-1 1Z"}]],DH=[["path",{d:"M17 21v-1a2 2 0 00-2-2H9a2 2 0 00-2 2v1"}],["path",{d:"M19 10h1a2 2 0 012 2v1"}],["path",{d:"M5 10H4a2 2 0 00-2 2v1"}],["circle",{cx:"12",cy:"11",r:"3"}],["circle",{cx:"18",cy:"4",r:"2"}],["circle",{cx:"6",cy:"4",r:"2"}]],FH=[["path",{d:"M20 11v6"}],["path",{d:"M20 13h2"}],["path",{d:"M3 21v-2a4 4 0 0 1 4-4h6a4 4 0 0 1 2.072.578"}],["circle",{cx:"10",cy:"7",r:"4"}],["circle",{cx:"20",cy:"19",r:"2"}]],RH=[["path",{d:"M19 16v-2a2 2 0 0 0-4 0v2"}],["path",{d:"M9.5 15H7a4 4 0 0 0-4 4v2"}],["circle",{cx:"10",cy:"7",r:"4"}],["rect",{x:"13",y:"16",width:"8",height:"5",rx:".899"}]],bH=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["circle",{cx:"9",cy:"7",r:"4"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11"}]],TH=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["circle",{cx:"9",cy:"7",r:"4"}],["line",{x1:"19",x2:"19",y1:"8",y2:"14"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11"}]],qH=[["path",{d:"M11.5 15H7a4 4 0 0 0-4 4v2"}],["path",{d:"M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}],["circle",{cx:"10",cy:"7",r:"4"}]],UH=[["path",{d:"m19 16-3 3"}],["path",{d:"M2 21a8 8 0 0 1 12.664-6.5"}],["path",{d:"M22 19h-6l3 3"}],["circle",{cx:"10",cy:"8",r:"5"}]],Fa=[["path",{d:"M2 21a8 8 0 0 1 13.292-6"}],["circle",{cx:"10",cy:"8",r:"5"}],["path",{d:"m16 19 2 2 4-4"}]],Ra=[["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.228 16.852-.923-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"M2 21a8 8 0 0 1 10.434-7.62"}],["path",{d:"m20.772 16.852.924-.383"}],["path",{d:"m20.772 19.148.924.383"}],["circle",{cx:"10",cy:"8",r:"5"}],["circle",{cx:"18",cy:"18",r:"3"}]],OH=[["path",{d:"M17 21a5 5 0 00-10 0"}],["path",{d:"M22 10.5a3.5 3.5 0 00-5.507-2.868"}],["path",{d:"M7.507 7.632A3.5 3.5 0 002 10.5"}],["circle",{cx:"12",cy:"13",r:"3"}],["circle",{cx:"18.5",cy:"4.5",r:"2.5"}],["circle",{cx:"5.5",cy:"4.5",r:"2.5"}]],GH=[["path",{d:"M19 11v6"}],["path",{d:"M19 13h2"}],["path",{d:"M2 21a8 8 0 0 1 12.868-6.349"}],["circle",{cx:"10",cy:"8",r:"5"}],["circle",{cx:"19",cy:"19",r:"2"}]],ba=[["path",{d:"M2 21a8 8 0 0 1 13.292-6"}],["circle",{cx:"10",cy:"8",r:"5"}],["path",{d:"M22 19h-6"}]],ZH=[["path",{d:"M2 21a8 8 0 0 1 10.821-7.487"}],["path",{d:"M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}],["circle",{cx:"10",cy:"8",r:"5"}]],Ta=[["path",{d:"M2 21a8 8 0 0 1 13.292-6"}],["circle",{cx:"10",cy:"8",r:"5"}],["path",{d:"M19 16v6"}],["path",{d:"M22 19h-6"}]],EH=[["circle",{cx:"10",cy:"8",r:"5"}],["path",{d:"M2 21a8 8 0 0 1 10.434-7.62"}],["circle",{cx:"18",cy:"18",r:"3"}],["path",{d:"m22 22-1.9-1.9"}]],WH=[["path",{d:"M16.051 12.616a1 1 0 0 1 1.909.024l.737 1.452a1 1 0 0 0 .737.535l1.634.256a1 1 0 0 1 .588 1.806l-1.172 1.168a1 1 0 0 0-.282.866l.259 1.613a1 1 0 0 1-1.541 1.134l-1.465-.75a1 1 0 0 0-.912 0l-1.465.75a1 1 0 0 1-1.539-1.133l.258-1.613a1 1 0 0 0-.282-.867l-1.156-1.152a1 1 0 0 1 .572-1.822l1.633-.256a1 1 0 0 0 .737-.535z"}],["path",{d:"M2 21a8 8 0 0 1 6.778-7.905"}],["path",{d:"M8.927 12.885a5 5 0 1 1 6.061-4.535"}]],qa=[["path",{d:"m16.5 16.5 5 5"}],["path",{d:"M2 21a8 8 0 0 1 11.531-7.18"}],["path",{d:"m21.5 16.5-5 5"}],["circle",{cx:"10",cy:"8",r:"5"}]],Ua=[["circle",{cx:"12",cy:"8",r:"5"}],["path",{d:"M20 21a8 8 0 0 0-16 0"}]],IH=[["circle",{cx:"10",cy:"7",r:"4"}],["path",{d:"M10.3 15H7a4 4 0 0 0-4 4v2"}],["circle",{cx:"17",cy:"17",r:"3"}],["path",{d:"m21 21-1.9-1.9"}]],XH=[["path",{d:"M10 15H6a4 4 0 0 0-4 4v2"}],["path",{d:"M22 17.5c0 2.499-1.75 3.749-3.83 4.474a.5.5 0 0 1-.335-.005c-2.085-.72-3.835-1.97-3.835-4.47V14a.5.5 0 0 1 .5-.499c1 0 2.25-.6 3.12-1.36a.6.6 0 0 1 .76-.001c.875.765 2.12 1.36 3.12 1.36a.5.5 0 0 1 .5.5z"}],["circle",{cx:"9",cy:"7",r:"4"}]],NH=[["path",{d:"M16.051 12.616a1 1 0 0 1 1.909.024l.737 1.452a1 1 0 0 0 .737.535l1.634.256a1 1 0 0 1 .588 1.806l-1.172 1.168a1 1 0 0 0-.282.866l.259 1.613a1 1 0 0 1-1.541 1.134l-1.465-.75a1 1 0 0 0-.912 0l-1.465.75a1 1 0 0 1-1.539-1.133l.258-1.613a1 1 0 0 0-.282-.866l-1.156-1.153a1 1 0 0 1 .572-1.822l1.633-.256a1 1 0 0 0 .737-.535z"}],["path",{d:"M8 15H7a4 4 0 0 0-4 4v2"}],["circle",{cx:"10",cy:"7",r:"4"}]],KH=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["circle",{cx:"9",cy:"7",r:"4"}],["line",{x1:"17",x2:"22",y1:"8",y2:"13"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13"}]],JH=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"}],["circle",{cx:"12",cy:"7",r:"4"}]],QH=[["path",{d:"M12.925 3.047a9 9 0 017.616 6.11"}],["path",{d:"M13 22a2 2 0 012-2h4a2 2 0 012 2"}],["path",{d:"M3 12a2 2 0 012-2h4a2 2 0 012 2"}],["path",{d:"M3.889 15.9a9 9 0 005.247 4.632"}],["circle",{cx:"17",cy:"14",r:"2"}],["circle",{cx:"7",cy:"4",r:"2"}]],jH=[["path",{d:"M20.453 8.911a9 9 0 00-6.696-5.738"}],["path",{d:"M21 22a4 4 0 00-8 0"}],["path",{d:"M3 12a4 4 0 017.963-.543"}],["path",{d:"M3.889 15.9a9 9 0 005.247 4.632"}],["circle",{cx:"17",cy:"15",r:"3"}],["circle",{cx:"7",cy:"5",r:"3"}]],Oa=[["path",{d:"M18 21a8 8 0 0 0-16 0"}],["circle",{cx:"10",cy:"8",r:"5"}],["path",{d:"M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"}]],YH=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87"}],["circle",{cx:"9",cy:"7",r:"4"}]],Ga=[["path",{d:"m16 2-2.3 2.3a3 3 0 0 0 0 4.2l1.8 1.8a3 3 0 0 0 4.2 0L22 8"}],["path",{d:"M15 15 3.3 3.3a4.2 4.2 0 0 0 0 6l7.3 7.3c.7.7 2 .7 2.8 0L15 15Zm0 0 7 7"}],["path",{d:"m2.1 21.8 6.4-6.3"}],["path",{d:"m19 5-7 7"}]],Za=[["path",{d:"M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2"}],["path",{d:"M7 2v20"}],["path",{d:"M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7"}]],_H=[["path",{d:"M12 2v20"}],["path",{d:"M2 5h20"}],["path",{d:"M3 3v2"}],["path",{d:"M7 3v2"}],["path",{d:"M17 3v2"}],["path",{d:"M21 3v2"}],["path",{d:"m19 5-7 7-7-7"}]],xH=[["path",{d:"M13 6v5a1 1 0 0 0 1 1h6.102a1 1 0 0 1 .712.298l.898.91a1 1 0 0 1 .288.702V17a1 1 0 0 1-1 1h-3"}],["path",{d:"M5 18H3a1 1 0 0 1-1-1V8a2 2 0 0 1 2-2h12c1.1 0 2.1.8 2.4 1.8l1.176 4.2"}],["path",{d:"M9 18h5"}],["circle",{cx:"16",cy:"18",r:"2"}],["circle",{cx:"7",cy:"18",r:"2"}]],aV=[["path",{d:"M8 21s-4-3-4-9 4-9 4-9"}],["path",{d:"M16 3s4 3 4 9-4 9-4 9"}],["line",{x1:"15",x2:"9",y1:"9",y2:"15"}],["line",{x1:"9",x2:"15",y1:"9",y2:"15"}]],hV=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor"}],["path",{d:"m7.9 7.9 2.7 2.7"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor"}],["path",{d:"m13.4 10.6 2.7-2.7"}],["circle",{cx:"7.5",cy:"16.5",r:".5",fill:"currentColor"}],["path",{d:"m7.9 16.1 2.7-2.7"}],["circle",{cx:"16.5",cy:"16.5",r:".5",fill:"currentColor"}],["path",{d:"m13.4 13.4 2.7 2.7"}],["circle",{cx:"12",cy:"12",r:"2"}]],tV=[["path",{d:"m12.828 4.813 5.344 2.375"}],["path",{d:"m15.769 18.153 3.461-8.306"}],["path",{d:"m5.687 14.074 7.625 4.852"}],["path",{d:"M9.772 5.579 5.228 11.42"}],["circle",{cx:"11",cy:"4",r:"2"}],["circle",{cx:"15",cy:"20",r:"2"}],["circle",{cx:"20",cy:"8",r:"2"}],["circle",{cx:"4",cy:"13",r:"2"}]],dV=[["path",{d:"M17.055 4.533a24 24 0 00-10.11 0"}],["path",{d:"M19.467 17.055a24 24 0 000-10.11"}],["path",{d:"M4.533 6.945a24 24 0 000 10.11"}],["path",{d:"M6.945 19.467a24 24 0 0010.11 0"}],["circle",{cx:"19",cy:"19",r:"2"}],["circle",{cx:"19",cy:"5",r:"2"}],["circle",{cx:"5",cy:"19",r:"2"}],["circle",{cx:"5",cy:"5",r:"2"}]],cV=[["path",{d:"M16 8q6 0 6-6-6 0-6 6"}],["path",{d:"M17.41 3.59a10 10 0 1 0 3 3"}],["path",{d:"M2 2a26.6 26.6 0 0 1 10 20c.9-6.82 1.5-9.5 4-14"}]],MV=[["path",{d:"M18 11c-1.5 0-2.5.5-3 2"}],["path",{d:"M4 6a2 2 0 0 0-2 2v4a5 5 0 0 0 5 5 8 8 0 0 1 5 2 8 8 0 0 1 5-2 5 5 0 0 0 5-5V8a2 2 0 0 0-2-2h-3a8 8 0 0 0-5 2 8 8 0 0 0-5-2z"}],["path",{d:"M6 11c1.5 0 2.5.5 3 2"}]],pV=[["path",{d:"M10 20h4"}],["path",{d:"M12 16v6"}],["path",{d:"M17 2h4v4"}],["path",{d:"m21 2-5.46 5.46"}],["circle",{cx:"12",cy:"11",r:"5"}]],lV=[["path",{d:"M12 15v7"}],["path",{d:"M9 19h6"}],["circle",{cx:"12",cy:"9",r:"6"}]],iV=[["path",{d:"m2 8 2 2-2 2 2 2-2 2"}],["path",{d:"m22 8-2 2 2 2-2 2 2 2"}],["path",{d:"M8 8v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2"}],["path",{d:"M16 10.34V6c0-.55-.45-1-1-1h-4.34"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],nV=[["path",{d:"m2 8 2 2-2 2 2 2-2 2"}],["path",{d:"m22 8-2 2 2 2-2 2 2 2"}],["rect",{width:"8",height:"14",x:"8",y:"5",rx:"1"}]],eV=[["path",{d:"M10.66 6H14a2 2 0 0 1 2 2v2.5l5.248-3.062A.5.5 0 0 1 22 7.87v8.196"}],["path",{d:"M16 16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2"}],["path",{d:"m2 2 20 20"}]],rV=[["path",{d:"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5"}],["rect",{x:"2",y:"6",width:"14",height:"12",rx:"2"}]],oV=[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2"}],["path",{d:"M2 8h20"}],["circle",{cx:"8",cy:"14",r:"2"}],["path",{d:"M8 12h8"}],["circle",{cx:"16",cy:"14",r:"2"}]],vV=[["path",{d:"M21 17v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2"}],["path",{d:"M21 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2"}],["circle",{cx:"12",cy:"12",r:"1"}],["path",{d:"M18.944 12.33a1 1 0 0 0 0-.66 7.5 7.5 0 0 0-13.888 0 1 1 0 0 0 0 .66 7.5 7.5 0 0 0 13.888 0"}]],$V=[["path",{d:"M10.01 10h.01"}],["path",{d:"M12 14.991h.01"}],["path",{d:"M12 22v-3"}],["path",{d:"M12 2v3"}],["path",{d:"M13 22h-2"}],["path",{d:"M13 2h-2"}],["path",{d:"m16.5 19.794-1-1.733"}],["path",{d:"m16.5 4.205-1 1.732"}],["path",{d:"M18.891 13.235a7 7 0 00-8.126-8.126"}],["path",{d:"m19.794 7.5-1.732 1"}],["path",{d:"M2 12h3"}],["path",{d:"M2 13v-2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M22 12h-3"}],["path",{d:"M22 13v-2"}],["path",{d:"m4.206 16.5 1.732-1"}],["path",{d:"m4.206 7.5 1.732 1"}],["path",{d:"M7.05 7.05a7 7 0 009.9 9.9"}],["path",{d:"m7.5 19.794 1-1.733"}],["path",{d:"M9 12h.01"}]],mV=[["path",{d:"M12 14.991h.01"}],["path",{d:"M12 22v-3"}],["path",{d:"M12 2v3"}],["path",{d:"M13 22h-2"}],["path",{d:"M13 2h-2"}],["path",{d:"M13.99 10H14"}],["path",{d:"m16.5 19.794-1-1.733"}],["path",{d:"m16.5 4.205-1 1.732"}],["path",{d:"m19.794 16.5-1.732-1"}],["path",{d:"m19.794 7.5-1.732 1"}],["path",{d:"M2 12h3"}],["path",{d:"M2 13v-2"}],["path",{d:"M22 12h-3"}],["path",{d:"M22 13v-2"}],["path",{d:"m4.206 16.5 1.732-1"}],["path",{d:"m4.206 7.5 1.732 1"}],["path",{d:"m7.5 19.794 1-1.733"}],["path",{d:"m7.5 4.205 1 1.732"}],["path",{d:"M9 12h.01"}],["circle",{cx:"12",cy:"12",r:"7"}]],yV=[["path",{d:"M11 7a16 16 20 0 1 10.98 4.362"}],["path",{d:"M12 12a13 13 0 0 1-8.66 5"}],["path",{d:"M16.83 13.634a16 16 0 0 1-9.267 7.328"}],["path",{d:"M20.66 17A13 13 0 0 0 12 12a13 13 0 0 1 0-10"}],["path",{d:"M8.17 15.366a16 16 0 0 1-1.713-11.69"}],["circle",{cx:"12",cy:"12",r:"10"}]],sV=[["circle",{cx:"6",cy:"12",r:"4"}],["circle",{cx:"18",cy:"12",r:"4"}],["line",{x1:"6",x2:"18",y1:"16",y2:"16"}]],CV=[["path",{d:"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"}],["path",{d:"M16 9a5 5 0 0 1 0 6"}]],uV=[["path",{d:"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"}],["path",{d:"M16 9a5 5 0 0 1 0 6"}],["path",{d:"M19.364 18.364a9 9 0 0 0 0-12.728"}]],gV=[["path",{d:"M16 9a5 5 0 0 1 .95 2.293"}],["path",{d:"M19.364 5.636a9 9 0 0 1 1.889 9.96"}],["path",{d:"m2 2 20 20"}],["path",{d:"m7 7-.587.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298V11"}],["path",{d:"M9.828 4.172A.686.686 0 0 1 11 4.657v.686"}]],AV=[["path",{d:"M11 4.702a.7.7 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.7.7 0 0 0 11 19.298z"}],["path",{d:"m16.5 14.5 5-5"}],["path",{d:"m16.5 9.5 5 5"}]],HV=[["path",{d:"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"}]],VV=[["path",{d:"m9 12 2 2 4-4"}],["path",{d:"M5 7c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v12H5V7Z"}],["path",{d:"M22 19H2"}]],SV=[["path",{d:"M3 11h3.75a2 2 0 0 1 1.6.8l.45.6a4 4 0 0 0 6.4 0l.45-.6a2 2 0 0 1 1.6-.8H21"}],["path",{d:"M3 7h18"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}]],Ea=[["path",{d:"M17 14h.01"}],["path",{d:"M7 7h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14"}]],wV=[["path",{d:"M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"}],["path",{d:"M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"}]],LV=[["path",{d:"M12 17v4"}],["path",{d:"M8 21h8"}],["path",{d:"m9 17 6.1-6.1a2 2 0 0 1 2.81.01L22 15"}],["circle",{cx:"8",cy:"9",r:"2"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2"}]],Wa=[["path",{d:"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72"}],["path",{d:"m14 7 3 3"}],["path",{d:"M5 6v4"}],["path",{d:"M19 14v4"}],["path",{d:"M10 2v2"}],["path",{d:"M7 8H3"}],["path",{d:"M21 16h-4"}],["path",{d:"M11 3H9"}]],fV=[["path",{d:"M15 4V2"}],["path",{d:"M15 16v-2"}],["path",{d:"M8 9h2"}],["path",{d:"M20 9h2"}],["path",{d:"M17.8 11.8 19 13"}],["path",{d:"M15 9h.01"}],["path",{d:"M17.8 6.2 19 5"}],["path",{d:"m3 21 9-9"}],["path",{d:"M12.2 6.2 11 5"}]],kV=[["path",{d:"M18 21V10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v11"}],["path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 1.132-1.803l7.95-3.974a2 2 0 0 1 1.837 0l7.948 3.974A2 2 0 0 1 22 8z"}],["path",{d:"M6 13h12"}],["path",{d:"M6 17h12"}]],PV=[["path",{d:"M3 6h3"}],["path",{d:"M17 6h.01"}],["rect",{width:"18",height:"20",x:"3",y:"2",rx:"2"}],["circle",{cx:"12",cy:"13",r:"5"}],["path",{d:"M12 18a2.5 2.5 0 0 0 0-5 2.5 2.5 0 0 1 0-5"}]],BV=[["path",{d:"M12 10L12 2"}],["path",{d:"M16 6L12 10L8 6"}],["path",{d:"M2 15C2.6 15.5 3.2 16 4.5 16C7 16 7 14 9.5 14C12.1 14 11.9 16 14.5 16C17 16 17 14 19.5 14C20.8 14 21.4 14.5 22 15"}],["path",{d:"M2 21C2.6 21.5 3.2 22 4.5 22C7 22 7 20 9.5 20C12.1 20 11.9 22 14.5 22C17 22 17 20 19.5 20C20.8 20 21.4 20.5 22 21"}]],zV=[["path",{d:"M12 10v2.2l1.6 1"}],["path",{d:"m16.13 7.66-.81-4.05a2 2 0 0 0-2-1.61h-2.68a2 2 0 0 0-2 1.61l-.78 4.05"}],["path",{d:"m7.88 16.36.8 4a2 2 0 0 0 2 1.61h2.72a2 2 0 0 0 2-1.61l.81-4.05"}],["circle",{cx:"12",cy:"12",r:"6"}]],DV=[["path",{d:"M12 2v8"}],["path",{d:"M2 15c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}],["path",{d:"M2 21c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}],["path",{d:"m8 6 4-4 4 4"}]],Ia=[["path",{d:"M2 12q2.5 2 5 0t5 0 5 0 5 0"}],["path",{d:"M2 19q2.5 2 5 0t5 0 5 0 5 0"}],["path",{d:"M2 5q2.5 2 5 0t5 0 5 0 5 0"}]],FV=[["path",{d:"M19 5a2 2 0 0 0-2 2v11"}],["path",{d:"M2 18c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"}],["path",{d:"M7 13h10"}],["path",{d:"M7 9h10"}],["path",{d:"M9 5a2 2 0 0 0-2 2v11"}]],RV=[["path",{d:"M12 2q2 2.5 0 5t0 5 0 5 0 5"}],["path",{d:"M19 2q2 2.5 0 5t0 5 0 5 0 5"}],["path",{d:"M5 2q2 2.5 0 5t0 5 0 5 0 5"}]],bV=[["path",{d:"m10.586 5.414-5.172 5.172"}],["path",{d:"m18.586 13.414-5.172 5.172"}],["path",{d:"M6 12h12"}],["circle",{cx:"12",cy:"20",r:"2"}],["circle",{cx:"12",cy:"4",r:"2"}],["circle",{cx:"20",cy:"12",r:"2"}],["circle",{cx:"4",cy:"12",r:"2"}]],TV=[["path",{d:"M12 22v-4"}],["path",{d:"M12.754 7.096a3 3 0 0 1 2.15 2.15"}],["path",{d:"M12.863 12.873a3 3 0 0 1-3.736-3.735"}],["path",{d:"M16.566 16.57A8 8 0 0 1 5.43 5.433"}],["path",{d:"m2 2 20 20"}],["path",{d:"M7 22h10"}],["path",{d:"M8.478 2.817a8 8 0 0 1 10.705 10.705"}]],qV=[["circle",{cx:"12",cy:"10",r:"8"}],["circle",{cx:"12",cy:"10",r:"3"}],["path",{d:"M7 22h10"}],["path",{d:"M12 22v-4"}]],UV=[["path",{d:"M17 17h-5c-1.09-.02-1.94.92-2.5 1.9A3 3 0 1 1 2.57 15"}],["path",{d:"M9 3.4a4 4 0 0 1 6.52.66"}],["path",{d:"m6 17 3.1-5.8a2.5 2.5 0 0 0 .057-2.05"}],["path",{d:"M20.3 20.3a4 4 0 0 1-2.3.7"}],["path",{d:"M18.6 13a4 4 0 0 1 3.357 3.414"}],["path",{d:"m12 6 .6 1"}],["path",{d:"m2 2 20 20"}]],OV=[["path",{d:"M6.5 8a2 2 0 0 0-1.906 1.46L2.1 18.5A2 2 0 0 0 4 21h16a2 2 0 0 0 1.925-2.54L19.4 9.5A2 2 0 0 0 17.48 8z"}],["path",{d:"M7.999 15a2.5 2.5 0 0 1 4 0 2.5 2.5 0 0 0 4 0"}],["circle",{cx:"12",cy:"5",r:"3"}]],GV=[["circle",{cx:"12",cy:"5",r:"3"}],["path",{d:"M6.5 8a2 2 0 0 0-1.905 1.46L2.1 18.5A2 2 0 0 0 4 21h16a2 2 0 0 0 1.925-2.54L19.4 9.5A2 2 0 0 0 17.48 8Z"}]],ZV=[["path",{d:"m2 22 10-10"}],["path",{d:"m16 8-1.17 1.17"}],["path",{d:"M3.47 12.53 5 11l1.53 1.53a3.5 3.5 0 0 1 0 4.94L5 19l-1.53-1.53a3.5 3.5 0 0 1 0-4.94Z"}],["path",{d:"m8 8-.53.53a3.5 3.5 0 0 0 0 4.94L9 15l1.53-1.53c.55-.55.88-1.25.98-1.97"}],["path",{d:"M10.91 5.26c.15-.26.34-.51.56-.73L13 3l1.53 1.53a3.5 3.5 0 0 1 .28 4.62"}],["path",{d:"M20 2h2v2a4 4 0 0 1-4 4h-2V6a4 4 0 0 1 4-4Z"}],["path",{d:"M11.47 17.47 13 19l-1.53 1.53a3.5 3.5 0 0 1-4.94 0L5 19l1.53-1.53a3.5 3.5 0 0 1 4.94 0Z"}],["path",{d:"m16 16-.53.53a3.5 3.5 0 0 1-4.94 0L9 15l1.53-1.53a3.49 3.49 0 0 1 1.97-.98"}],["path",{d:"M18.74 13.09c.26-.15.51-.34.73-.56L21 11l-1.53-1.53a3.5 3.5 0 0 0-4.62-.28"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],EV=[["path",{d:"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2"}],["path",{d:"m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06"}],["path",{d:"m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8"}]],WV=[["path",{d:"M2 22 16 8"}],["path",{d:"M3.47 12.53 5 11l1.53 1.53a3.5 3.5 0 0 1 0 4.94L5 19l-1.53-1.53a3.5 3.5 0 0 1 0-4.94Z"}],["path",{d:"M7.47 8.53 9 7l1.53 1.53a3.5 3.5 0 0 1 0 4.94L9 15l-1.53-1.53a3.5 3.5 0 0 1 0-4.94Z"}],["path",{d:"M11.47 4.53 13 3l1.53 1.53a3.5 3.5 0 0 1 0 4.94L13 11l-1.53-1.53a3.5 3.5 0 0 1 0-4.94Z"}],["path",{d:"M20 2h2v2a4 4 0 0 1-4 4h-2V6a4 4 0 0 1 4-4Z"}],["path",{d:"M11.47 17.47 13 19l-1.53 1.53a3.5 3.5 0 0 1-4.94 0L5 19l1.53-1.53a3.5 3.5 0 0 1 4.94 0Z"}],["path",{d:"M15.47 13.47 17 15l-1.53 1.53a3.5 3.5 0 0 1-4.94 0L9 15l1.53-1.53a3.5 3.5 0 0 1 4.94 0Z"}],["path",{d:"M19.47 9.47 21 11l-1.53 1.53a3.5 3.5 0 0 1-4.94 0L13 11l1.53-1.53a3.5 3.5 0 0 1 4.94 0Z"}]],IV=[["path",{d:"M10 6v4"}],["path",{d:"M21 6a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-5.675A7 7 0 1 1 9 6z"}]],XV=[["circle",{cx:"7",cy:"12",r:"3"}],["path",{d:"M10 9v6"}],["circle",{cx:"17",cy:"12",r:"3"}],["path",{d:"M14 7v8"}],["path",{d:"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1"}]],NV=[["path",{d:"m14.305 19.53.923-.382"}],["path",{d:"m15.228 16.852-.923-.383"}],["path",{d:"m16.852 15.228-.383-.923"}],["path",{d:"m16.852 20.772-.383.924"}],["path",{d:"m19.148 15.228.383-.923"}],["path",{d:"m19.53 21.696-.382-.924"}],["path",{d:"M2 8.82a15 15 0 0120 0"}],["path",{d:"m20.772 16.852.924-.383"}],["path",{d:"m20.772 19.148.924.383"}],["path",{d:"M5 12.859a10 10 0 0110.18-2.342"}],["path",{d:"M8.5 16.429a5 5 0 011.998-1.2"}],["circle",{cx:"18",cy:"18",r:"3"}]],KV=[["path",{d:"M12 20h.01"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0"}]],JV=[["path",{d:"M2 8.82a15 15 0 0120 0"}],["path",{d:"M20 16v-2a2 2 0 00-4 0v2"}],["path",{d:"M5 12.859a10 10 0 018.436-2.756"}],["path",{d:"M8.5 16.429a5 5 0 011.794-1.13"}],["rect",{x:"14",y:"16",width:"8",height:"5",rx:"1"}]],QV=[["path",{d:"M12 20h.01"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0"}]],jV=[["path",{d:"M12 20h.01"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764"}],["path",{d:"m2 2 20 20"}]],YV=[["path",{d:"M2 8.82a15 15 0 0 1 20 0"}],["path",{d:"M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"}],["path",{d:"M5 12.859a10 10 0 0 1 10.5-2.222"}],["path",{d:"M8.5 16.429a5 5 0 0 1 3-1.406"}]],_V=[["path",{d:"M11.965 10.105v4L13.5 12.5a5 5 0 0 1 8 1.5"}],["path",{d:"M11.965 14.105h4"}],["path",{d:"M17.965 18.105h4L20.43 19.71a5 5 0 0 1-8-1.5"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0"}],["path",{d:"M21.965 22.105v-4"}],["path",{d:"M5 12.86a10 10 0 0 1 3-2.032"}],["path",{d:"M8.5 16.429h.01"}]],xV=[["path",{d:"M12 20h.01"}]],aS=[["path",{d:"M12 20h.01"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0"}]],hS=[["path",{d:"M10 2v8"}],["path",{d:"M12.8 21.6A2 2 0 1 0 14 18H2"}],["path",{d:"M17.5 10a2.5 2.5 0 1 1 2 4H2"}],["path",{d:"m6 6 4 -4 4 4"}]],tS=[["path",{d:"M12.8 19.6A2 2 0 1 0 14 16H2"}],["path",{d:"M17.5 8a2.5 2.5 0 1 1 2 4H2"}],["path",{d:"M9.8 4.4A2 2 0 1 1 11 8H2"}]],dS=[["path",{d:"M10 2v8"}],["path",{d:"M12.8 21.6A2 2 0 1 0 14 18H2"}],["path",{d:"M17.5 10a2.5 2.5 0 1 1 2 4H2"}],["path",{d:"m6 6 4 4 4-4"}]],cS=[["path",{d:"M8 22h8"}],["path",{d:"M7 10h3m7 0h-1.343"}],["path",{d:"M12 15v7"}],["path",{d:"M7.307 7.307A12.33 12.33 0 0 0 7 10a5 5 0 0 0 7.391 4.391M8.638 2.981C8.75 2.668 8.872 2.34 9 2h6c1.5 4 2 6 2 8 0 .407-.05.809-.145 1.198"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22"}]],MS=[["path",{d:"M8 22h8"}],["path",{d:"M7 10h10"}],["path",{d:"M12 15v7"}],["path",{d:"M12 15a5 5 0 0 0 5-5c0-2-.5-4-2-8H9c-1.5 4-2 6-2 8a5 5 0 0 0 5 5Z"}]],pS=[["rect",{width:"8",height:"8",x:"3",y:"3",rx:"2"}],["path",{d:"M7 11v4a2 2 0 0 0 2 2h4"}],["rect",{width:"8",height:"8",x:"13",y:"13",rx:"2"}]],lS=[["path",{d:"m19 12-1.5 3"}],["path",{d:"M19.63 18.81 22 20"}],["path",{d:"M6.47 8.23a1.68 1.68 0 0 1 2.44 1.93l-.64 2.08a6.76 6.76 0 0 0 10.16 7.67l.42-.27a1 1 0 1 0-2.73-4.21l-.42.27a1.76 1.76 0 0 1-2.63-1.99l.64-2.08A6.66 6.66 0 0 0 3.94 3.9l-.7.4a1 1 0 1 0 2.55 4.34z"}]],iS=[["path",{d:"M10.747 5.093a6 6 0 0 1 6.841-2.882c.438.12.54.662.219.984L14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-2.882 6.842"}],["path",{d:"m13.5 13.5-7.88 7.88a1 1 0 0 1-2.999-3l7.88-7.88"}],["path",{d:"m2 2 20 20"}]],nS=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z"}]],eS=[["path",{d:"M18 4H6"}],["path",{d:"M18 8 6 20"}],["path",{d:"m6 8 12 12"}]],rS=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]],oS=[["path",{d:"M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z"}]],vS=[["path",{d:"M10.768 5.111 13.44 2.44a1.5 1.5 0 012.474 1.561l-1.633 4.625"}],["path",{d:"m18.889 13.232.672-.672A1.5 1.5 0 0018.5 10h-2.844"}],["path",{d:"m2 2 20 20"}],["path",{d:"m7.94 7.94-3.5 3.499A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l5.5-5.5"}]],$S=[["path",{d:"m2 10 2.456-3.684a.7.7 0 0 1 1.106-.013l2.39 3.413a.7.7 0 0 0 1.096-.001l2.402-3.432a.7.7 0 0 1 1.098 0l2.402 3.432a.7.7 0 0 0 1.098 0l2.389-3.413a.7.7 0 0 1 1.106.013L22 10"}],["path",{d:"m2 18.002 2.456-3.684a.7.7 0 0 1 1.106-.013l2.39 3.413a.7.7 0 0 0 1.097 0l2.402-3.432a.7.7 0 0 1 1.098 0l2.402 3.432a.7.7 0 0 0 1.098 0l2.389-3.413a.7.7 0 0 1 1.106.013L22 18.002"}]],mS=[["path",{d:"M12 7.5a4.5 4.5 0 1 1 5 4.5"}],["path",{d:"M7 12a4.5 4.5 0 1 1 5-4.5V21"}]],yS=[["path",{d:"M21 14.5A9 6.5 0 0 1 5.5 19"}],["path",{d:"M3 9.5A9 6.5 0 0 1 18.5 5"}],["circle",{cx:"17.5",cy:"14.5",r:"3.5"}],["circle",{cx:"6.5",cy:"9.5",r:"3.5"}]],sS=[["path",{d:"M11 21a3 3 0 0 0 3-3V6.5a1 1 0 0 0-7 0"}],["path",{d:"M7 19V6a3 3 0 0 0-3-3h0"}],["circle",{cx:"17",cy:"17",r:"3"}]],CS=[["path",{d:"M16 4.525v14.948"}],["path",{d:"M20 3A17 17 0 0 1 4 3"}],["path",{d:"M4 21a17 17 0 0 1 16 0"}],["path",{d:"M8 4.525v14.948"}]],uS=[["path",{d:"M10 16c0-4-3-4.5-3-8a5 5 0 0 1 10 0c0 3.466-3 6.196-3 10a3 3 0 0 0 6 0"}],["circle",{cx:"7",cy:"16",r:"3"}]],gS=[["path",{d:"M3 10A6.06 6.06 0 0 1 12 10 A6.06 6.06 0 0 0 21 10"}],["path",{d:"M6 3v12a6 6 0 0 0 12 0V3"}]],AS=[["path",{d:"M3 16h6.857c.162-.012.19-.323.038-.38a6 6 0 1 1 4.212 0c-.153.057-.125.368.038.38H21"}],["path",{d:"M3 20h18"}]],HS=[["path",{d:"M19 21a15 15 0 0 1 0-18"}],["path",{d:"M20 12H4"}],["path",{d:"M5 3a15 15 0 0 1 0 18"}]],VS=[["path",{d:"M15 3h6v6"}],["path",{d:"M21 3 3 21"}],["path",{d:"m9 9 6 6"}]],SS=[["path",{d:"M10 19V5.5a1 1 0 0 1 5 0V17a2 2 0 0 0 2 2h5l-3-3"}],["path",{d:"m22 19-3 3"}],["path",{d:"M5 19V5.5a1 1 0 0 1 5 0"}],["path",{d:"M5 5.5A2.5 2.5 0 0 0 2.5 3"}]],wS=[["circle",{cx:"12",cy:"15",r:"6"}],["path",{d:"M18 3A6 6 0 0 1 6 3"}]],LS=[["path",{d:"M11 5.5a1 1 0 0 1 5 0V16a5 5 0 0 0 5 5"}],["path",{d:"M16 11.5a1 1 0 0 1 5 0V16a5 5 0 0 1-5 5"}],["path",{d:"M6 19V6a3 3 0 0 0-3-3h0"}],["path",{d:"M6 5.5a1 1 0 0 1 5 0V19"}]],fS=[["circle",{cx:"11",cy:"11",r:"8"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65"}],["line",{x1:"11",x2:"11",y1:"8",y2:"14"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11"}]],kS=[["circle",{cx:"11",cy:"11",r:"8"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11"}]];var PS=Object.freeze({__proto__:null,AArrowDown:Ya,AArrowUp:_a,ALargeSmall:xa,Accessibility:ah,Activity:hh,ActivitySquare:C0,Ad:th,AirVent:dh,Airplay:ch,AlarmCheck:C,AlarmClock:ph,AlarmClockCheck:C,AlarmClockMinus:u,AlarmClockOff:Mh,AlarmClockPlus:g,AlarmMinus:u,AlarmPlus:g,AlarmSmoke:lh,Album:b0,AlertCircle:j,AlertOctagon:X2,AlertTriangle:ka,AlignCenter:ga,AlignCenterHorizontal:ih,AlignCenterVertical:nh,AlignEndHorizontal:eh,AlignEndVertical:rh,AlignHorizontalDistributeCenter:oh,AlignHorizontalDistributeEnd:vh,AlignHorizontalDistributeStart:$h,AlignHorizontalJustifyCenter:mh,AlignHorizontalJustifyEnd:yh,AlignHorizontalJustifyStart:sh,AlignHorizontalSpaceAround:Ch,AlignHorizontalSpaceBetween:uh,AlignJustify:Ha,AlignLeft:s,AlignRight:Aa,AlignStartHorizontal:gh,AlignStartVertical:Hh,AlignVerticalDistributeCenter:Ah,AlignVerticalDistributeEnd:Vh,AlignVerticalDistributeStart:Sh,AlignVerticalJustifyCenter:wh,AlignVerticalJustifyEnd:Lh,AlignVerticalJustifyStart:kh,AlignVerticalSpaceAround:fh,AlignVerticalSpaceBetween:Ph,Ambulance:Bh,Ampersand:zh,Ampersands:Dh,Amphora:Fh,Anchor:Rh,Angle:bh,Angry:Z1,Annoyed:G1,Antenna:Th,Anvil:qh,Aperture:Uh,AppWindow:Gh,AppWindowMac:Oh,Apple:Zh,Archive:Wh,ArchiveRestore:Eh,ArchiveX:Xh,AreaChart:q,Armchair:Ih,ArmenianDram:Nh,ArrowBigDown:Jh,ArrowBigDownDash:Kh,ArrowBigLeft:jh,ArrowBigLeftDash:Qh,ArrowBigRight:_h,ArrowBigRightDash:Yh,ArrowBigUp:at,ArrowBigUpDash:xh,ArrowDown:et,ArrowDown01:ht,ArrowDown10:tt,ArrowDownAZ:A,ArrowDownAz:A,ArrowDownCircle:_,ArrowDownFromLine:dt,ArrowDownLeft:ct,ArrowDownLeftFromCircle:x,ArrowDownLeftFromSquare:V0,ArrowDownLeftSquare:u0,ArrowDownNarrowWide:Mt,ArrowDownRight:pt,ArrowDownRightFromCircle:a1,ArrowDownRightFromSquare:S0,ArrowDownRightSquare:g0,ArrowDownSquare:A0,ArrowDownToDot:lt,ArrowDownToLine:it,ArrowDownUp:nt,ArrowDownWideNarrow:H,ArrowDownZA:V,ArrowDownZa:V,ArrowLeft:$t,ArrowLeftCircle:Y,ArrowLeftFromLine:rt,ArrowLeftRight:vt,ArrowLeftSquare:H0,ArrowLeftToLine:ot,ArrowRight:Ct,ArrowRightCircle:d1,ArrowRightFromLine:mt,ArrowRightLeft:yt,ArrowRightSquare:f0,ArrowRightToLine:st,ArrowUp:kt,ArrowUp01:gt,ArrowUp10:ut,ArrowUpAZ:S,ArrowUpAz:S,ArrowUpCircle:c1,ArrowUpDown:At,ArrowUpFromDot:Ht,ArrowUpFromLine:Vt,ArrowUpLeft:St,ArrowUpLeftFromCircle:h1,ArrowUpLeftFromSquare:w0,ArrowUpLeftSquare:k0,ArrowUpNarrowWide:w,ArrowUpRight:wt,ArrowUpRightFromCircle:t1,ArrowUpRightFromSquare:L0,ArrowUpRightSquare:P0,ArrowUpSquare:B0,ArrowUpToLine:Lt,ArrowUpWideNarrow:ft,ArrowUpZA:L,ArrowUpZa:L,ArrowsUpFromLine:Pt,Asterisk:zt,AsteriskSquare:z0,Astroid:Bt,AtSign:Dt,Atom:Ft,AudioLines:Tt,AudioLinesOff:Rt,AudioLinesX:bt,AudioWaveform:qt,Avocado:Ut,Award:Ot,Axe:Gt,Axis3D:f,Axis3d:f,Baby:Zt,Backpack:Et,Bacon:Wt,Badge:M5,BadgeAlert:It,BadgeCent:Xt,BadgeCheck:k,BadgeDollarSign:Nt,BadgeEuro:Kt,BadgeHelp:P,BadgeIndianRupee:Jt,BadgeInfo:Qt,BadgeJapaneseYen:jt,BadgeMinus:Yt,BadgePercent:_t,BadgePlus:xt,BadgePoundSterling:a5,BadgeQuestionMark:P,BadgeRussianRuble:h5,BadgeSwissFranc:t5,BadgeTurkishLira:d5,BadgeX:c5,BaggageClaim:p5,Balloon:l5,Ban:i5,Banana:n5,Bandage:e5,BangladeshiTaka:r5,Banknote:y5,BanknoteArrowDown:o5,BanknoteArrowUp:v5,BanknoteCheck:$5,BanknoteX:m5,BarChart:X,BarChart2:N,BarChart3:W,BarChart4:E,BarChartBig:Z,BarChartHorizontal:O,BarChartHorizontalBig:U,Barcode:s5,Barrel:C5,Baseline:u5,Bath:g5,Battery:f5,BatteryCharging:A5,BatteryFull:H5,BatteryLow:V5,BatteryMedium:S5,BatteryPlus:w5,BatteryWarning:L5,Beaker:k5,Bean:B5,BeanOff:P5,Bed:F5,BedDouble:z5,BedSingle:D5,Beef:b5,BeefOff:R5,Beer:q5,BeerOff:T5,BelarusianRuble:U5,Bell:N5,BellCheck:O5,BellDot:G5,BellElectric:Z5,BellMinus:E5,BellOff:W5,BellPlus:I5,BellRing:X5,BetweenHorizonalEnd:B,BetweenHorizonalStart:z,BetweenHorizontalEnd:B,BetweenHorizontalStart:z,BetweenVerticalEnd:K5,BetweenVerticalStart:J5,BicepsFlexed:Q5,Bike:j5,Binary:Y5,Binoculars:x5,Biohazard:_5,Bird:a4,Birdhouse:h4,Bitcoin:t4,Blend:d4,Blender:c4,Blinds:M4,Blocks:p4,Bluetooth:e4,BluetoothConnected:l4,BluetoothOff:i4,BluetoothSearching:n4,Bold:r4,Bolt:o4,Bomb:v4,Bone:m4,BoneFracture:$4,Book:O4,BookA:y4,BookAlert:s4,BookAudio:C4,BookBookmark:D,BookCheck:u4,BookCopy:g4,BookDashed:F,BookDown:A4,BookHeadphones:H4,BookHeart:V4,BookImage:S4,BookKey:w4,BookLock:L4,BookMarked:D,BookMinus:f4,BookOpen:B4,BookOpenCheck:k4,BookOpenText:P4,BookPlus:D4,BookSearch:z4,BookTemplate:F,BookText:F4,BookType:R4,BookUp:T4,BookUp2:b4,BookUser:q4,BookX:U4,Bookmark:X4,BookmarkCheck:G4,BookmarkMinus:Z4,BookmarkOff:E4,BookmarkPlus:W4,BookmarkX:I4,BoomBox:N4,Bot:Q4,BotMessageSquare:K4,BotOff:J4,BottleWine:j4,BowArrow:Y4,Box:_4,BoxSelect:X0,Boxes:x4,Braces:R,Brackets:a3,Brain:c3,BrainCircuit:h3,BrainCog:t3,BrainOff:d3,BrickWall:l3,BrickWallFire:M3,BrickWallShield:p3,Bridge:i3,Briefcase:v3,BriefcaseBusiness:n3,BriefcaseConveyorBelt:e3,BriefcaseMedical:r3,BriefcasePlus:o3,BringToFront:$3,Broccoli:m3,Broom:C3,BroomSparkles:y3,Brush:u3,BrushCleaning:s3,Bubbles:g3,Bug:H3,BugOff:A3,BugPlay:V3,Building:w3,Building2:b,BuildingComplex:b,BuildingComplexPlus:S3,Bus:k3,BusFront:L3,Cable:f3,CableCar:P3,Cake:z3,CakeSlice:B3,Calculator:D3,Calendar:hd,Calendar1:F3,CalendarArrowDown:R3,CalendarArrowUp:b3,CalendarCheck:q3,CalendarCheck2:T3,CalendarChevronsLeft:U3,CalendarChevronsRight:O3,CalendarClock:G3,CalendarCog:E3,CalendarDays:Z3,CalendarFold:W3,CalendarHeart:I3,CalendarMinus:N3,CalendarMinus2:X3,CalendarOff:K3,CalendarPlus:_3,CalendarPlus2:J3,CalendarRange:Q3,CalendarSearch:j3,CalendarSync:Y3,CalendarX:ad,CalendarX2:x3,Calendars:td,Camera:dd,CameraOff:cd,Can:pd,CanSoda:Md,CandlestickChart:G,Candy:nd,CandyCane:ld,CandyOff:id,Cannabis:rd,CannabisOff:ed,Captions:T,CaptionsOff:od,Car:yd,CarBattery:vd,CarFront:$d,CarTaxiFront:md,Caravan:sd,CardSim:Cd,Carrot:ud,Carton:Ad,CartonOff:gd,CaseLower:Hd,CaseSensitive:Vd,CaseUpper:Sd,CassetteTape:wd,Cast:Ld,Castle:fd,Cat:kd,Cctv:Bd,CctvOff:Pd,Certificate:zd,ChartArea:q,ChartBar:O,ChartBarBig:U,ChartBarDecreasing:Fd,ChartBarIncreasing:Dd,ChartBarStacked:Rd,ChartCandlestick:G,ChartColumn:W,ChartColumnBig:Z,ChartColumnDecreasing:bd,ChartColumnIncreasing:E,ChartColumnStacked:Td,ChartGantt:qd,ChartLine:I,ChartNetwork:Ud,ChartNoAxesColumn:N,ChartNoAxesColumnDecreasing:Od,ChartNoAxesColumnIncreasing:X,ChartNoAxesCombined:Gd,ChartNoAxesGantt:K,ChartPie:J,ChartScatter:Q,ChartSpline:Zd,Check:Id,CheckCheck:Ed,CheckCircle:M1,CheckCircle2:p1,CheckLine:Wd,CheckSquare:T0,CheckSquare2:q0,ChefHat:Xd,Cherry:Nd,ChessBishop:Jd,ChessKing:Kd,ChessKnight:Qd,ChessPawn:jd,ChessQueen:Yd,ChessRook:_d,ChevronDown:xd,ChevronDownCircle:l1,ChevronDownSquare:O0,ChevronFirst:a6,ChevronLast:h6,ChevronLeft:t6,ChevronLeftCircle:i1,ChevronLeftSquare:Z0,ChevronRight:d6,ChevronRightCircle:n1,ChevronRightSquare:U0,ChevronUp:c6,ChevronUpCircle:e1,ChevronUpSquare:G0,ChevronsDown:p6,ChevronsDownUp:M6,ChevronsLeft:n6,ChevronsLeftRight:i6,ChevronsLeftRightEllipsis:l6,ChevronsRight:r6,ChevronsRightLeft:e6,ChevronsUp:$6,ChevronsUpDown:o6,Church:v6,Cigarette:y6,CigaretteOff:m6,Circle:U6,CircleAlert:j,CircleArrowDown:_,CircleArrowLeft:Y,CircleArrowOutDownLeft:x,CircleArrowOutDownRight:a1,CircleArrowOutUpLeft:h1,CircleArrowOutUpRight:t1,CircleArrowRight:d1,CircleArrowUp:c1,CircleCheck:p1,CircleCheckBig:M1,CircleChevronDown:l1,CircleChevronLeft:i1,CircleChevronRight:n1,CircleChevronUp:e1,CircleChevronsDown:s6,CircleChevronsLeft:C6,CircleChevronsRight:u6,CircleChevronsUp:g6,CircleDashed:H6,CircleDashedCheck:A6,CircleDivide:r1,CircleDollarSign:V6,CircleDot:w6,CircleDotDashed:S6,CircleEllipsis:L6,CircleEqual:f6,CircleEuro:k6,CircleFadingArrowUp:P6,CircleFadingPlay:B6,CircleFadingPlus:z6,CircleGauge:o1,CircleHelp:n,CircleMinus:v1,CircleOff:D6,CircleParking:m1,CircleParkingOff:$1,CirclePause:y1,CirclePercent:s1,CirclePile:F6,CirclePlay:C1,CirclePlus:u1,CirclePoundSterling:R6,CirclePower:g1,CircleQuestionMark:n,CircleSlash:b6,CircleSlash2:A1,CircleSlashed:A1,CircleSmall:T6,CircleStar:q6,CircleStop:H1,CircleUser:S1,CircleUserRound:V1,CircleX:w1,CircuitBoard:O6,Citrus:G6,Clapperboard:Z6,ClefAlto:E6,ClefBass:W6,ClefTreble:I6,Clipboard:a8,ClipboardCheck:X6,ClipboardClock:N6,ClipboardCopy:K6,ClipboardEdit:f1,ClipboardList:J6,ClipboardMinus:Q6,ClipboardPaste:j6,ClipboardPen:f1,ClipboardPenLine:L1,ClipboardPlus:Y6,ClipboardSignature:L1,ClipboardType:_6,ClipboardX:x6,Clock:A8,Clock1:h8,Clock10:t8,Clock11:d8,Clock12:c8,Clock2:M8,Clock3:p8,Clock4:l8,Clock5:i8,Clock6:n8,Clock7:e8,Clock8:r8,Clock9:o8,ClockAlert:v8,ClockArrowDown:$8,ClockArrowLeft:m8,ClockArrowRight:y8,ClockArrowUp:s8,ClockCheck:C8,ClockFading:u8,ClockPlus:g8,ClosedCaption:H8,Cloud:E8,CloudAlert:V8,CloudBackup:S8,CloudCheck:w8,CloudCog:L8,CloudDownload:k1,CloudDrizzle:f8,CloudFog:k8,CloudHail:P8,CloudLightning:z8,CloudMoon:D8,CloudMoonRain:B8,CloudOff:F8,CloudRain:b8,CloudRainWind:R8,CloudSnow:T8,CloudSun:q8,CloudSunRain:U8,CloudSync:O8,CloudUpload:P1,Cloudy:G8,Clover:Z8,Club:W8,Code:I8,Code2:B1,CodeSquare:E0,CodeXml:B1,Coffee:X8,Cog:N8,Coins:K8,Columns:z1,Columns2:z1,Columns3:D1,Columns3Cog:e,Columns4:J8,ColumnsSettings:e,Combine:Q8,Command:j8,Compass:Y8,Component:_8,Computer:x8,ConciergeBell:hc,Cone:ac,Construction:tc,Contact:dc,Contact2:F1,ContactRound:F1,Container:cc,Contrast:Mc,Cookie:pc,CookingPot:lc,Copy:vc,CopyCheck:ic,CopyMinus:nc,CopyPlus:ec,CopySlash:rc,CopyX:oc,Copyleft:$c,Copyright:mc,CornerDownLeft:yc,CornerDownRight:sc,CornerLeftDown:Cc,CornerLeftUp:uc,CornerRightDown:gc,CornerRightUp:Hc,CornerUpLeft:Ac,CornerUpRight:Vc,Cpu:wc,CpuAi:Sc,CreativeCommons:Lc,CreditCard:Dc,CreditCardCheck:fc,CreditCardMinus:kc,CreditCardPlus:Pc,CreditCardReader:Bc,CreditCardX:zc,Croissant:Fc,Crop:Rc,Cross:bc,Crosshair:Tc,Crown:qc,Cuboid:Uc,CupSoda:Gc,Cupcake:Oc,CurlyBraces:R,Currency:Zc,Cylinder:Ec,Dam:Wc,Database:xc,DatabaseArrowDown:Ic,DatabaseArrowUp:Xc,DatabaseBackup:Nc,DatabaseCheck:Kc,DatabaseMinus:Qc,DatabasePlus:Jc,DatabaseSearch:jc,DatabaseX:Yc,DatabaseZap:_c,DecimalsArrowLeft:a7,DecimalsArrowRight:h7,Delete:d7,Dessert:t7,Diameter:c7,Diamond:l7,DiamondMinus:M7,DiamondPercent:R1,DiamondPlus:p7,Dice1:i7,Dice2:n7,Dice3:e7,Dice4:r7,Dice5:m7,Dice6:o7,Dices:v7,Diff:$7,Disc:u7,Disc2:y7,Disc3:s7,DiscAlbum:C7,Divide:g7,DivideCircle:r1,DivideSquare:N0,Dna:H7,DnaOff:A7,Dock:V7,Dog:S7,DollarSign:w7,Dome:L7,Donut:f7,DoorClosed:z7,DoorClosedCog:k7,DoorClosedLocked:P7,DoorClosedPackage:B7,DoorOpen:D7,DoorStairwell:F7,Dot:R7,DotSquare:J0,Download:b7,DownloadCloud:k1,DraftingCompass:T7,Drama:q7,Drill:U7,Drone:O7,Droplet:Z7,DropletOff:G7,Droplets:E7,Drum:W7,Drumstick:I7,Dumbbell:X7,Ear:K7,EarOff:N7,Earth:b1,EarthLock:J7,Eclipse:Q7,Edit:l,Edit2:c0,Edit3:d0,Egg:_7,EggFried:j7,EggOff:Y7,EighthNote:T1,EighthNotes:q1,Eject:x7,Ellipse:a9,Ellipsis:O1,EllipsisVertical:U1,Engine:h9,Equal:M9,EqualApproximately:d9,EqualApproximatelyNot:t9,EqualNot:c9,EqualSquare:K0,Eraser:i9,EthernetPort:p9,Euro:l9,EvCharger:n9,Expand:e9,ExternalLink:r9,Eye:m9,EyeClosed:o9,EyeDashed:v9,EyeOff:$9,FaceAngry:Z1,FaceExpressionless:G1,FaceGrinning:E1,FaceNeutral:W1,FaceSlightlyFrowning:I1,FaceSlightlySmiling:N1,FaceSlightlySmilingPlus:X1,Factory:y9,Fan:s9,FastForward:C9,Faucet:u9,Feather:A9,Fence:g9,FerrisWheel:H9,File:Y9,FileArchive:V9,FileAudio:r,FileAudio2:r,FileAxis3D:K1,FileAxis3d:K1,FileBadge:J1,FileBadge2:J1,FileBarChart:Y1,FileBarChart2:_1,FileBox:S9,FileBraces:j1,FileBracesCorner:Q1,FileChartColumn:_1,FileChartColumnIncreasing:Y1,FileChartLine:x1,FileChartPie:h2,FileCheck:w9,FileCheck2:a2,FileCheckCorner:a2,FileClock:L9,FileCode:f9,FileCode2:t2,FileCodeCorner:t2,FileCog:d2,FileCog2:d2,FileDiff:k9,FileDigit:P9,FileDown:B9,FileEdit:n2,FileExclamationPoint:c2,FileHeadphone:r,FileHeart:z9,FileImage:D9,FileInput:F9,FileJson:j1,FileJson2:Q1,FileKey:p2,FileKey2:p2,FileLineChart:x1,FileLock:M2,FileLock2:M2,FileMinus:R9,FileMinus2:l2,FileMinusCorner:l2,FileMusic:b9,FileOutput:T9,FilePen:n2,FilePenLine:i2,FilePieChart:h2,FilePlay:e2,FilePlus:q9,FilePlus2:r2,FilePlusCorner:r2,FileQuestion:o2,FileQuestionMark:o2,FileScan:U9,FileSearch:O9,FileSearch2:v2,FileSearchCorner:v2,FileSignal:$2,FileSignature:i2,FileSliders:G9,FileSpreadsheet:Z9,FileStack:E9,FileSymlink:W9,FileTerminal:I9,FileText:X9,FileType:N9,FileType2:m2,FileTypeCorner:m2,FileUp:K9,FileUser:J9,FileVideo:e2,FileVideo2:y2,FileVideoCamera:y2,FileVolume:Q9,FileVolume2:$2,FileWarning:c2,FileX:j9,FileX2:s2,FileXCorner:s2,Files:_9,Film:x9,Filter:H2,FilterX:A2,Fingerprint:C2,FingerprintPattern:C2,FireExtinguisher:aM,Fish:dM,FishOff:hM,FishSymbol:tM,FishingHook:cM,FishingRod:MM,Flag:nM,FlagOff:pM,FlagTriangleLeft:lM,FlagTriangleRight:iM,Flame:rM,FlameKindling:eM,Flashlight:vM,FlashlightOff:oM,FlaskConical:mM,FlaskConicalOff:$M,FlaskRound:yM,FlipHorizontal:F0,FlipHorizontal2:Pa,FlipVertical:R0,FlipVertical2:Ba,Flower:CM,Flower2:sM,Focus:uM,FoldHorizontal:gM,FoldVertical:AM,Folder:_M,FolderArchive:HM,FolderBookmark:VM,FolderCheck:SM,FolderClock:wM,FolderClosed:LM,FolderCode:fM,FolderCog:u2,FolderCog2:u2,FolderDot:kM,FolderDown:PM,FolderEdit:g2,FolderGit:zM,FolderGit2:BM,FolderHeart:DM,FolderInput:FM,FolderKanban:RM,FolderKey:bM,FolderLock:TM,FolderMinus:qM,FolderOpen:OM,FolderOpenDot:UM,FolderOutput:GM,FolderPen:g2,FolderPlus:ZM,FolderRoot:EM,FolderSearch:IM,FolderSearch2:WM,FolderSymlink:XM,FolderSync:NM,FolderTree:KM,FolderUp:JM,FolderUser:QM,FolderUsers:jM,FolderX:YM,Folders:xM,Footprints:ap,ForkKnife:Za,ForkKnifeCrossed:Ga,Forklift:hp,Form:tp,FormInput:p0,Forward:dp,Frame:cp,Frown:I1,Fuel:Mp,Fullscreen:pp,FunctionSquare:Q0,Funnel:H2,FunnelPlus:lp,FunnelX:A2,Galaxy:ip,GalleryHorizontal:np,GalleryHorizontalEnd:ep,GalleryThumbnails:rp,GalleryVertical:vp,GalleryVerticalEnd:op,Gamepad:yp,Gamepad2:$p,GamepadDirectional:mp,GanttChart:K,GanttChartSquare:m,GapHorizontal:sp,GapVertical:Cp,Gauge:up,GaugeCircle:o1,Gavel:gp,Gem:Ap,GeorgianLari:Vp,Germ:Sp,GermOff:Hp,Ghost:wp,Gift:Lp,GitBranch:Pp,GitBranchMinus:fp,GitBranchPlus:kp,GitCommit:V2,GitCommitHorizontal:V2,GitCommitVertical:Bp,GitCompare:Dp,GitCompareArrows:zp,GitFork:Fp,GitGraph:Rp,GitMerge:Tp,GitMergeConflict:bp,GitPullRequest:Ep,GitPullRequestArrow:qp,GitPullRequestClosed:Gp,GitPullRequestCreate:Op,GitPullRequestCreateArrow:Up,GitPullRequestDraft:Zp,GlassWater:Wp,Glasses:Ip,Globe:jp,Globe2:b1,GlobeCheck:Xp,GlobeCode:Np,GlobeLock:Kp,GlobeOff:Jp,GlobeX:Qp,Goal:Yp,Gpu:_p,Grab:P2,GraduationCap:xp,Grape:al,GraphHub:hl,GraphStar:tl,Grid:o,Grid2X2:f2,Grid2X2Check:S2,Grid2X2Plus:w2,Grid2X2X:L2,Grid2x2:f2,Grid2x2Check:S2,Grid2x2Plus:w2,Grid2x2X:L2,Grid3X3:o,Grid3x2:dl,Grid3x3:o,Grip:pl,GripHorizontal:cl,GripVertical:Ml,Groceries:ll,Group:il,Guitar:nl,GuitarElectric:el,HalfNote:k2,Ham:rl,Hamburger:ol,Hammer:vl,Hand:ul,HandCoins:$l,HandFist:ml,HandGrab:P2,HandHeart:yl,HandHelping:B2,HandMetal:sl,HandPlatter:Cl,Handbag:gl,HandleBottomRight:Al,Handshake:Hl,HardDrive:wl,HardDriveDownload:Vl,HardDriveUpload:Sl,HardHat:Ll,Hash:fl,HatGlasses:kl,Haze:Bl,Hd:Pl,HdmiPort:zl,Heading:Ul,Heading1:Dl,Heading2:Fl,Heading3:Rl,Heading4:bl,Heading5:Tl,Heading6:ql,HeadphoneOff:Ol,Headphones:Gl,Headset:Zl,Heart:Ql,HeartCrack:El,HeartHandshake:Wl,HeartMinus:Il,HeartOff:Xl,HeartPlus:Nl,HeartPulse:Kl,HeartX:Jl,Heater:jl,Helicopter:Yl,HelpCircle:n,HelpingHand:B2,Hexagon:_l,Highlighter:xl,HikingStick:ai,History:i0,Home:z2,Hop:hi,HopOff:ti,Hospital:di,Hotel:ci,Hourglass:pi,HourglassCog:Mi,House:z2,HouseCog:li,HouseHeart:ii,HousePlug:ei,HousePlus:ni,HouseWifi:ri,Houses:oi,IceCream:F2,IceCream2:D2,IceCreamBowl:D2,IceCreamCone:F2,IdCard:si,IdCardLanyard:vi,Image:Hi,ImageDown:$i,ImageMinus:mi,ImageOff:yi,ImagePlay:Ci,ImagePlus:ui,ImageUp:gi,ImageUpscale:Ai,Images:Vi,Import:Si,Inbox:fi,Indent:$,IndentDecrease:v,IndentIncrease:$,IndianRupee:wi,Infinity:Li,Info:ki,Inspect:ha,InspectionPanel:Pi,Italic:Bi,IterationCcw:zi,IterationCw:Fi,IvBag:Di,JapaneseYen:Ri,Joystick:bi,Kanban:qi,KanbanSquare:j0,KanbanSquareDashed:I0,Kayak:Ti,KazakhTenge:Ui,Key:Zi,KeyRound:Oi,KeySquare:Gi,Keyboard:Wi,KeyboardMusic:Ei,KeyboardOff:Ii,Lambda:Xi,Lamp:Yi,LampCeiling:Ni,LampDesk:Ki,LampFloor:Ji,LampWallDown:Qi,LampWallUp:ji,LandPlot:_i,Landmark:xi,Languages:an,Laptop:tn,Laptop2:R2,LaptopMinimal:R2,LaptopMinimalCheck:hn,Lasso:Mn,LassoSelect:dn,Laugh:E1,LayerArrowDown:cn,LayerArrowUp:pn,Layers:b2,Layers2:ln,Layers3:b2,LayersArrowDown:nn,LayersArrowUp:en,LayersMinus:rn,LayersPlus:on,Layout:t0,LayoutArrowDown:vn,LayoutArrowRight:$n,LayoutDashboard:mn,LayoutFreeform:yn,LayoutGrid:un,LayoutGridCircles:sn,LayoutList:Cn,LayoutPanelLeft:gn,LayoutPanelTop:An,LayoutTemplate:Hn,Leaf:Vn,LeafyGreen:Sn,Lectern:wn,Lens:kn,LensConcave:Ln,LensConvex:fn,LetterSpacing:Pn,LetterText:Va,Letters:Bn,Library:Dn,LibraryBig:zn,LibrarySquare:Y0,LifeBuoy:Rn,Ligature:Fn,Lightbulb:Tn,LightbulbOff:bn,Lighthouse:qn,LineChart:I,LineDotBottomVertical:Un,LineDotLeftHorizontal:On,LineDotRightHorizontal:Gn,LineDotTopVertical:Zn,LineHeight:En,LineSquiggle:Wn,LineStyle:In,Link:Kn,Link2:Nn,Link2Off:Xn,List:me,ListCheck:Jn,ListChecks:Qn,ListChevronsDownUp:jn,ListChevronsUpDown:Yn,ListClock:_n,ListCollapse:xn,ListEnd:ae,ListFilter:te,ListFilterPlus:he,ListIndentDecrease:v,ListIndentIncrease:$,ListMinus:de,ListMusic:ce,ListOrdered:Me,ListPlus:pe,ListRestart:le,ListSortAscending:ie,ListSortDescending:ee,ListStart:re,ListTodo:ne,ListTree:oe,ListVideo:ve,ListX:$e,Loader:se,Loader2:T2,LoaderCircle:T2,LoaderPinwheel:ye,Locate:ue,LocateFixed:Ce,LocateOff:ge,LocationEdit:G2,Lock:He,LockKeyhole:Ae,LockKeyholeOpen:q2,LockOpen:U2,LogIn:Ve,LogOut:we,Logs:Se,Lollipop:Le,Luggage:fe,MSquare:_0,Magnet:ke,Mail:Ge,MailBadge:Pe,MailCheck:Be,MailClock:ze,MailDot:De,MailMinus:Fe,MailOpen:Re,MailPen:be,MailPlus:Te,MailQuestion:O2,MailQuestionMark:O2,MailSearch:qe,MailWarning:Ue,MailX:Oe,Mailbox:Ie,Mails:Ze,Map:cr,MapMinus:Ee,MapPin:dr,MapPinCheck:We,MapPinCheckInside:Xe,MapPinHouse:Ne,MapPinMinus:Je,MapPinMinusInside:Ke,MapPinOff:je,MapPinPen:G2,MapPinPlus:_e,MapPinPlusInside:Qe,MapPinSearch:Ye,MapPinX:ar,MapPinXInside:xe,MapPinned:hr,MapPlus:tr,Mars:pr,MarsStroke:Mr,Martini:nr,Maximize:ir,Maximize2:lr,Medal:er,Megaphone:or,MegaphoneOff:rr,Meh:W1,MemoryStick:vr,Menu:$r,MenuSquare:x0,Merge:mr,MessageCircle:kr,MessageCircleCheck:yr,MessageCircleCode:sr,MessageCircleDashed:ur,MessageCircleDashedCheck:Cr,MessageCircleHeart:gr,MessageCircleMore:Ar,MessageCircleOff:Hr,MessageCirclePlus:Vr,MessageCircleQuestion:Z2,MessageCircleQuestionMark:Z2,MessageCircleReply:wr,MessageCircleText:Sr,MessageCircleWarning:Lr,MessageCircleX:fr,MessageSquare:Xr,MessageSquareCheck:Pr,MessageSquareCode:zr,MessageSquareDashed:Br,MessageSquareDiff:Dr,MessageSquareDot:Fr,MessageSquareHeart:Rr,MessageSquareLock:br,MessageSquareMore:Tr,MessageSquareOff:qr,MessageSquarePlus:Ur,MessageSquareQuote:Zr,MessageSquareReply:Gr,MessageSquareShare:Or,MessageSquareText:Er,MessageSquareWarning:Wr,MessageSquareX:Ir,MessagesCircle:Nr,MessagesSquare:Kr,Metronome:Jr,Mic:Yr,Mic2:W2,MicAudioLines:Qr,MicOff:jr,MicSignal:E2,MicVocal:W2,Microchip:_r,Microscope:xr,Microwave:ao,MidiPort:ho,Milestone:to,Milk:po,MilkOff:co,Minimize:lo,Minimize2:Mo,Minus:io,MinusCircle:v1,MinusSquare:aa,MirrorRectangular:no,MirrorRound:eo,Monitor:wo,MonitorCheck:oo,MonitorCloud:ro,MonitorCog:vo,MonitorDot:$o,MonitorDown:mo,MonitorOff:yo,MonitorPause:so,MonitorPc:Co,MonitorPlay:uo,MonitorSmartphone:go,MonitorSpeaker:Ao,MonitorStop:Ho,MonitorUp:Vo,MonitorX:So,Moon:fo,MoonStar:Lo,Mop:Po,MopSparkles:ko,MoreHorizontal:O1,MoreVertical:U1,Mosque:Bo,Motorbike:zo,Mountain:Fo,MountainSnow:Do,Mouse:Eo,MouseLeft:Ro,MouseOff:bo,MousePointer:Go,MousePointer2:qo,MousePointer2Off:To,MousePointerBan:Uo,MousePointerClick:Oo,MousePointerSquareDashed:W0,MouseRight:Zo,Mouth:Io,MouthOff:Wo,Move:tv,Move3D:I2,Move3d:I2,MoveDiagonal:No,MoveDiagonal2:Xo,MoveDown:Qo,MoveDownLeft:Ko,MoveDownRight:Jo,MoveHorizontal:jo,MoveLeft:Yo,MoveRight:_o,MoveUp:hv,MoveUpLeft:xo,MoveUpRight:av,MoveVertical:dv,Music:q1,Music2:T1,Music3:k2,Music4:m0,Navigation:lv,Navigation2:Mv,Navigation2Off:cv,NavigationOff:pv,NepaliRupee:iv,Network:nv,Newspaper:ev,Nfc:rv,NonBinary:ov,Notebook:sv,NotebookDot:vv,NotebookPen:$v,NotebookTabs:mv,NotebookText:yv,NotepadText:uv,NotepadTextDashed:Cv,Nut:Av,NutOff:gv,Octagon:Vv,OctagonAlert:X2,OctagonMinus:Hv,OctagonPause:N2,OctagonX:K2,Omega:Sv,Option:wv,Orbit:Lv,Origami:fv,Outdent:v,Package:bv,Package2:kv,PackageCheck:Bv,PackageMinus:Pv,PackageOpen:zv,PackagePlus:Dv,PackageSearch:Fv,PackageX:Rv,PaintBucket:Tv,PaintRoller:qv,Paintbrush:Uv,Paintbrush2:J2,PaintbrushVertical:J2,Palette:Ov,Palmtree:fa,Panda:Gv,PanelBottom:Wv,PanelBottomClose:Zv,PanelBottomDashed:Q2,PanelBottomInactive:Q2,PanelBottomOpen:Ev,PanelLeft:x2,PanelLeftClose:j2,PanelLeftDashed:Y2,PanelLeftInactive:Y2,PanelLeftOpen:_2,PanelLeftRightDashed:Iv,PanelRight:Jv,PanelRightClose:Xv,PanelRightDashed:a0,PanelRightInactive:a0,PanelRightOpen:Nv,PanelTop:Yv,PanelTopBottomDashed:Kv,PanelTopClose:Qv,PanelTopDashed:h0,PanelTopInactive:h0,PanelTopOpen:jv,PanelsLeftBottom:_v,PanelsLeftRight:D1,PanelsRightBottom:xv,PanelsTopBottom:e0,PanelsTopLeft:t0,PaperBag:a$,Paperclip:h$,Parasol:t$,Parentheses:d$,Park:c$,ParkingCircle:m1,ParkingCircleOff:$1,ParkingMeter:M$,ParkingSquare:da,ParkingSquareOff:ta,PartyPopper:p$,Pause:l$,PauseCircle:y1,PauseOctagon:N2,PawPrint:i$,PcCase:n$,Pen:c0,PenBox:l,PenLine:d0,PenOff:e$,PenSquare:l,PenTool:r$,Pencil:y$,PencilLine:o$,PencilOff:v$,PencilRuler:$$,PencilSparkles:m$,Pentagon:s$,Percent:C$,PercentCircle:s1,PercentDiamond:R1,PercentSquare:ca,PerfumeBottle:u$,PersonStanding:A$,Phi:g$,PhilippinePeso:H$,Phone:B$,PhoneCall:V$,PhoneForwarded:w$,PhoneIncoming:S$,PhoneLog:f$,PhoneMissed:L$,PhoneOff:k$,PhoneOutgoing:P$,Pi:z$,PiSquare:Ma,Piano:D$,Pickaxe:F$,PictureInPicture:b$,PictureInPicture2:R$,PieChart:J,PiggyBank:T$,Pilcrow:O$,PilcrowLeft:q$,PilcrowRight:U$,PilcrowSquare:pa,Pill:E$,PillBottle:G$,Pin:W$,PinOff:Z$,Pipette:N$,Pizza:I$,Plane:J$,PlaneLanding:X$,PlaneTakeoff:K$,PlantPot:Q$,Play:Y$,PlayCircle:C1,PlayOff:j$,PlaySquare:la,PlayingCard:_$,PlayingCards:x$,PlayingCardsFan:am,Plug:tm,Plug2:hm,PlugZap:M0,PlugZap2:M0,Plus:dm,PlusCircle:u1,PlusSquare:ia,PocketKnife:cm,Podcast:E2,Podium:Mm,Pointer:lm,PointerOff:pm,Popcorn:im,Popsicle:nm,PoundSterling:em,Power:om,PowerCircle:g1,PowerOff:rm,PowerSquare:na,Presentation:vm,Printer:ym,Printer3d:$m,PrinterCheck:mm,PrinterX:sm,Projector:um,Proportions:Cm,Puzzle:gm,Pyramid:Am,QrCode:Hm,Quote:Vm,Rabbit:Sm,Radar:Lm,Radiation:wm,Radical:fm,Radio:zm,RadioOff:km,RadioReceiver:Pm,RadioTower:Bm,Radius:Dm,Rainbow:Fm,Rat:bm,Ratio:Rm,Receipt:Xm,ReceiptCent:Tm,ReceiptEuro:qm,ReceiptIndianRupee:Um,ReceiptJapaneseYen:Om,ReceiptPoundSterling:Zm,ReceiptRussianRuble:Gm,ReceiptSwissFranc:Em,ReceiptText:Wm,ReceiptTurkishLira:Im,RectangleCircle:Nm,RectangleEllipsis:p0,RectangleGoggles:Km,RectangleHorizontal:Jm,RectangleVertical:Qm,Recycle:jm,Redo:xm,Redo2:Ym,RedoDot:_m,RefreshCcw:hy,RefreshCcwDot:ay,RefreshCw:dy,RefreshCwOff:ty,Refrigerator:cy,Regex:py,RemoveFormatting:My,Repeat:ey,Repeat1:iy,Repeat2:ly,RepeatOff:ny,Replace:oy,ReplaceAll:ry,Reply:$y,ReplyAll:vy,Rewind:my,Ribbon:yy,Road:sy,RobotArm:Cy,RobotVacuum:uy,Rocket:gy,RockingChair:Ay,RollerCoaster:Hy,Rose:Vy,Rotate3D:l0,Rotate3d:l0,RotateCcw:Ly,RotateCcwClock:i0,RotateCcwKey:Sy,RotateCcwSquare:wy,RotateCw:By,RotateCwClock:fy,RotateCwFadingClock:ky,RotateCwSquare:Py,Route:Dy,RouteOff:zy,Router:Fy,Rows:n0,Rows2:n0,Rows3:e0,Rows4:Ry,Rss:by,RugbyBall:Ty,Ruler:Uy,RulerDimensionLine:qy,RussianRuble:Oy,Sailboat:Ey,Salad:Gy,Sandwich:Zy,Satellite:Iy,SatelliteDish:Wy,SaudiRiyal:Xy,Save:Yy,SaveAll:Ny,SaveCheck:Ky,SaveOff:Jy,SavePen:Qy,SavePlus:jy,Scale:_y,Scale3D:r0,Scale3d:r0,Scaling:as,Scan:es,ScanBarcode:xy,ScanBox:hs,ScanEye:ts,ScanFace:ds,ScanHeart:cs,ScanLine:Ms,ScanQrCode:ps,ScanSearch:ls,ScanSquare:is,ScanText:ns,ScatterChart:Q,School:rs,School2:Da,Scissors:vs,ScissorsLineDashed:os,ScissorsSquare:ea,ScissorsSquareDashedBottom:D0,Scooter:$s,ScratchBlocks:ms,ScreenShare:ss,ScreenShareOff:ys,Screw:Cs,Scroll:us,ScrollText:gs,Search:fs,SearchAlert:As,SearchCheck:Hs,SearchCode:Vs,SearchSlash:Ss,SearchX:ws,Section:Ls,Send:Ps,SendHorizonal:o0,SendHorizontal:o0,SendToBack:ks,SeparatorHorizontal:Bs,SeparatorVertical:zs,Server:qs,ServerCog:Ds,ServerCrash:Fs,ServerNas:Rs,ServerOff:bs,ServerPlus:Ts,Settings:Os,Settings2:Us,Shapes:Gs,Share:Zs,Share2:Es,Sheet:Ws,Shell:Is,ShelvingUnit:Xs,Shield:pC,ShieldAlert:Ns,ShieldBan:Ks,ShieldCheck:Js,ShieldClose:$0,ShieldCog:js,ShieldCogCorner:Qs,ShieldEllipsis:Ys,ShieldHalf:_s,ShieldHouse:aC,ShieldKeyhole:xs,ShieldLock:hC,ShieldMinus:tC,ShieldOff:dC,ShieldPlus:cC,ShieldQuestion:v0,ShieldQuestionMark:v0,ShieldUser:MC,ShieldX:$0,Ship:nC,ShipCargo:lC,ShipWheel:iC,Shirt:eC,ShoppingBag:rC,ShoppingBasket:vC,ShoppingCart:mC,ShoppingCartMinus:oC,ShoppingCartPlus:$C,Shovel:yC,ShowerHead:sC,Shredder:CC,Shrimp:gC,ShrimpOff:uC,Shrink:AC,Shrub:HC,Shuffle:VC,Sidebar:x2,SidebarClose:j2,SidebarOpen:_2,Sigma:SC,SigmaSquare:ra,Signal:PC,SignalHigh:wC,SignalLow:LC,SignalMedium:fC,SignalZero:kC,Signature:BC,Signpost:DC,SignpostBig:zC,Siren:FC,SixteenthNotes:m0,SkipBack:RC,SkipForward:bC,Skull:TC,Slash:qC,SlashSquare:oa,Slice:UC,Sliders:y0,SlidersHorizontal:OC,SlidersVertical:y0,Smartphone:WC,SmartphoneCharging:GC,SmartphoneNfc:ZC,SmartphoneUserRound:EC,Smile:N1,SmilePlus:X1,Snail:IC,Snowflake:XC,SoapDispenserDroplet:NC,Sofa:KC,SolarPanel:JC,SortAsc:w,SortDesc:H,Soup:QC,Space:jC,Spade:YC,Sparkle:_C,Sparkles:s0,Speaker:xC,Speech:hu,SpellCheck:tu,SpellCheck2:au,Spline:cu,SplinePointer:du,Split:Mu,SplitSquareHorizontal:va,SplitSquareVertical:$a,Spool:pu,SportShoe:lu,Spotlight:iu,SprayCan:nu,Sprout:eu,Square:zu,SquareActivity:C0,SquareArrowDown:A0,SquareArrowDownLeft:u0,SquareArrowDownRight:g0,SquareArrowLeft:H0,SquareArrowOutDownLeft:V0,SquareArrowOutDownRight:S0,SquareArrowOutUpLeft:w0,SquareArrowOutUpRight:L0,SquareArrowRight:f0,SquareArrowRightEnter:ru,SquareArrowRightExit:ou,SquareArrowUp:B0,SquareArrowUpLeft:k0,SquareArrowUpRight:P0,SquareAsterisk:z0,SquareBookmark:b0,SquareBottomDashedScissors:D0,SquareCenterlineDashedHorizontal:F0,SquareCenterlineDashedVertical:R0,SquareChartGantt:m,SquareCheck:q0,SquareCheckBig:T0,SquareChevronDown:O0,SquareChevronLeft:Z0,SquareChevronRight:U0,SquareChevronUp:G0,SquareCode:E0,SquareDashed:X0,SquareDashedBottom:$u,SquareDashedBottomCode:vu,SquareDashedKanban:I0,SquareDashedMousePointer:W0,SquareDashedPlus:mu,SquareDashedText:y,SquareDashedTopSolid:yu,SquareDashedX:Cu,SquareDashedXCorner:su,SquareDimensions:uu,SquareDivide:N0,SquareDot:J0,SquareEqual:K0,SquareExclamationPoint:gu,SquareFunction:Q0,SquareGanttChart:m,SquareKanban:j0,SquareLibrary:Y0,SquareM:_0,SquareMenu:x0,SquareMinus:aa,SquareMousePointer:ha,SquareOff:Au,SquareParking:da,SquareParkingOff:ta,SquarePause:Hu,SquarePen:l,SquarePercent:ca,SquarePi:Ma,SquarePilcrow:pa,SquarePlay:la,SquarePlus:ia,SquarePower:na,SquareRadical:Vu,SquareRoundCorner:Su,SquareScissors:ea,SquareSigma:ra,SquareSlash:oa,SquareSparkles:wu,SquareSplitHorizontal:va,SquareSplitVertical:$a,SquareSquare:Lu,SquareStack:fu,SquareStar:ku,SquareStop:Pu,SquareTerminal:ya,SquareText:Bu,SquareUser:sa,SquareUserRound:ma,SquareX:Ca,SquaresExclude:Du,SquaresIntersect:Fu,SquaresSubtract:Ru,SquaresUnite:bu,Squircle:Uu,SquircleDashed:Tu,Squirrel:qu,Stamp:Ou,Star:Nu,StarCheck:Gu,StarHalf:Zu,StarMinus:Eu,StarOff:Wu,StarPlus:Iu,StarX:Xu,Stars:s0,StepBack:Ku,StepForward:Ju,Stethoscope:Qu,Sticker:ju,StickyNote:tg,StickyNoteCheck:Yu,StickyNoteMinus:_u,StickyNoteOff:xu,StickyNotePlus:ag,StickyNoteX:hg,StickyNotes:dg,Stone:cg,StopCircle:H1,Store:Mg,StretchHorizontal:pg,StretchVertical:lg,Strikethrough:ig,Subscript:ng,Subtitles:T,Summary:eg,Sun:mg,SunDim:rg,SunMedium:og,SunMoon:vg,SunSnow:$g,Sunrise:yg,Sunset:sg,Superscript:Cg,SwatchBook:ug,SwissFranc:gg,SwitchCamera:Ag,Sword:Hg,Swords:Vg,Syringe:Sg,Table:zg,Table2:wg,TableCellsMerge:Lg,TableCellsSplit:fg,TableColumnsSplit:kg,TableConfig:e,TableOfContents:Pg,TableProperties:Bg,TableRowsSplit:Dg,Tablet:Rg,TabletSmartphone:Fg,Tablets:Tg,Tag:Og,TagPlus:bg,TagX:qg,Tags:Ug,Tally1:Gg,Tally2:Zg,Tally3:Eg,Tally4:Ig,Tally5:Wg,Tangent:Xg,Target:Ng,Telescope:Kg,Tent:jg,TentTree:Jg,Terminal:Qg,TerminalSquare:ya,TestTube:Yg,TestTube2:ua,TestTubeDiagonal:ua,TestTubes:_g,Text:s,TextAlignCenter:ga,TextAlignEnd:Aa,TextAlignJustify:Ha,TextAlignJustifyCenter:xg,TextAlignJustifyEnd:aA,TextAlignJustifyStart:hA,TextAlignStart:s,TextCursor:dA,TextCursorInput:tA,TextInitial:Va,TextQuote:cA,TextSearch:MA,TextSelect:y,TextSelection:y,TextWrap:Sa,Theater:pA,Thermometer:nA,ThermometerSnowflake:lA,ThermometerSun:iA,ThumbsDown:eA,ThumbsUp:rA,TicTacToe:oA,Ticket:uA,TicketCheck:vA,TicketMinus:$A,TicketPercent:mA,TicketPlus:yA,TicketSlash:sA,TicketX:CA,Tickets:HA,TicketsPlane:gA,Timeline:AA,Timer:wA,TimerOff:VA,TimerReset:SA,ToggleLeft:LA,ToggleRight:fA,Toilet:kA,Tombstone:PA,ToolCase:BA,Toolbox:zA,Toothbrush:FA,ToothbrushSparkles:DA,Tornado:RA,Torus:bA,Touchpad:qA,TouchpadOff:TA,TowelRack:OA,TowerControl:UA,ToyBrick:GA,Tractor:ZA,TrafficCone:EA,Trailer:WA,Train:wa,TrainFront:XA,TrainFrontTunnel:IA,TrainTrack:NA,TramFront:wa,Transgender:KA,Trash:La,Trash2:La,TrashOff:JA,TreeDeciduous:QA,TreePalm:fa,TreePine:jA,Trees:YA,TrendingDown:_A,TrendingUp:aH,TrendingUpDown:xA,Triangle:dH,TriangleAlert:ka,TriangleDashed:hH,TriangleRight:tH,TrianglesCenterlineDashedHorizontal:Pa,TrianglesCenterlineDashedVertical:Ba,Trophy:cH,Truck:pH,TruckElectric:MH,TubeLotion:lH,TurkishLira:iH,Turntable:nH,Turtle:eH,Tv:oH,Tv2:za,TvMinimal:za,TvMinimalPlay:rH,Type:sH,TypeOutline:vH,Umbrella:mH,UmbrellaOff:$H,Underline:yH,Undo:gH,Undo2:CH,UndoDot:uH,UnfoldHorizontal:AH,UnfoldVertical:HH,Ungroup:VH,University:Da,Unlink:wH,Unlink2:SH,Unlock:U2,UnlockKeyhole:q2,Unplug:LH,Upload:fH,UploadCloud:P1,Usb:zH,UsbCPort:kH,User:JH,User2:Ua,UserCheck:PH,UserCheck2:Fa,UserCircle:S1,UserCircle2:V1,UserCog:BH,UserCog2:Ra,UserGroup:DH,UserKey:FH,UserLock:RH,UserMinus:bH,UserMinus2:ba,UserPen:qH,UserPlus:TH,UserPlus2:Ta,UserRound:Ua,UserRoundArrowLeft:UH,UserRoundCheck:Fa,UserRoundCog:Ra,UserRoundGroup:OH,UserRoundKey:GH,UserRoundMinus:ba,UserRoundPen:ZH,UserRoundPlus:Ta,UserRoundSearch:EH,UserRoundStar:WH,UserRoundX:qa,UserSearch:IH,UserShield:XH,UserSquare:sa,UserSquare2:ma,UserStar:NH,UserX:KH,UserX2:qa,Users:YH,Users2:Oa,UsersCollaboration:QH,UsersRound:Oa,UsersRoundCollaboration:jH,Utensils:Za,UtensilsCrossed:Ga,UtilityPole:_H,Van:xH,Variable:aV,Vault:hV,VectorPolygon:tV,VectorSquare:dV,Vegan:cV,VenetianMask:MV,Venus:lV,VenusAndMars:pV,Verified:k,Vibrate:nV,VibrateOff:iV,Video:rV,VideoOff:eV,Videotape:oV,View:vV,Virus:mV,VirusOff:$V,Voicemail:sV,Volleyball:yV,Volume:HV,Volume1:CV,Volume2:uV,VolumeOff:gV,VolumeX:AV,Vote:VV,Wallet:wV,Wallet2:Ea,WalletCards:SV,WalletMinimal:Ea,Wallpaper:LV,Wand:fV,Wand2:Wa,WandSparkles:Wa,Warehouse:kV,WashingMachine:PV,Watch:zV,Waves:Ia,WavesArrowDown:BV,WavesArrowUp:DV,WavesHorizontal:Ia,WavesLadder:FV,WavesVertical:RV,Waypoints:bV,Webcam:qV,WebcamOff:TV,Webhook:EV,WebhookOff:UV,Weight:GV,WeightTilde:OV,Wheat:WV,WheatOff:ZV,Whistle:IV,WholeWord:XV,Wifi:aS,WifiCog:NV,WifiHigh:KV,WifiLock:JV,WifiLow:QV,WifiOff:jV,WifiPen:YV,WifiSync:_V,WifiZero:xV,Wind:tS,WindArrowDown:dS,WindArrowUp:hS,Wine:MS,WineOff:cS,Workflow:pS,Worm:lS,WrapText:Sa,Wrench:nS,WrenchOff:iS,X:rS,XCircle:w1,XLineTop:eS,XOctagon:K2,XSquare:Ca,Zap:oS,ZapOff:vS,ZodiacAquarius:$S,ZodiacAries:mS,ZodiacCancer:yS,ZodiacCapricorn:sS,ZodiacGemini:CS,ZodiacLeo:uS,ZodiacLibra:AS,ZodiacOphiuchus:gS,ZodiacPisces:HS,ZodiacSagittarius:VS,ZodiacScorpio:SS,ZodiacTaurus:wS,ZodiacVirgo:LS,ZoomIn:fS,ZoomOut:kS});const BS=({icons:h=PS,nameAttr:t="data-lucide",attrs:c={},root:d=document,inTemplates:M}={})=>{if(!Object.values(h).length)throw new Error(`Please provide an icons object.
+If you want to use all the icons you can import it like:
+ \`import { createIcons, icons } from 'lucide';
+lucide.createIcons({icons});\``);if(typeof d>"u")throw new Error("`createIcons()` only works in a browser environment.");if(Array.from(d.querySelectorAll(`[${t}]`)).forEach(p=>ja(p,{nameAttr:t,icons:h,attrs:c})),M&&Array.from(d.querySelectorAll("template")).forEach(p=>BS({icons:h,nameAttr:t,attrs:c,root:p.content,inTemplates:M})),t==="data-lucide"){const p=d.querySelectorAll("[icon-name]");p.length>0&&(console.warn("[Lucide] Some icons were found with the now deprecated icon-name attribute. These will still be replaced for backwards compatibility, but will no longer be supported in v1.0 and you should switch to data-lucide"),Array.from(p).forEach(Xa=>ja(Xa,{nameAttr:"icon-name",icons:h,attrs:c})))}};a.AArrowDown=Ya,a.AArrowUp=_a,a.ALargeSmall=xa,a.Accessibility=ah,a.Activity=hh,a.ActivitySquare=C0,a.Ad=th,a.AirVent=dh,a.Airplay=ch,a.AlarmCheck=C,a.AlarmClock=ph,a.AlarmClockCheck=C,a.AlarmClockMinus=u,a.AlarmClockOff=Mh,a.AlarmClockPlus=g,a.AlarmMinus=u,a.AlarmPlus=g,a.AlarmSmoke=lh,a.Album=b0,a.AlertCircle=j,a.AlertOctagon=X2,a.AlertTriangle=ka,a.AlignCenter=ga,a.AlignCenterHorizontal=ih,a.AlignCenterVertical=nh,a.AlignEndHorizontal=eh,a.AlignEndVertical=rh,a.AlignHorizontalDistributeCenter=oh,a.AlignHorizontalDistributeEnd=vh,a.AlignHorizontalDistributeStart=$h,a.AlignHorizontalJustifyCenter=mh,a.AlignHorizontalJustifyEnd=yh,a.AlignHorizontalJustifyStart=sh,a.AlignHorizontalSpaceAround=Ch,a.AlignHorizontalSpaceBetween=uh,a.AlignJustify=Ha,a.AlignLeft=s,a.AlignRight=Aa,a.AlignStartHorizontal=gh,a.AlignStartVertical=Hh,a.AlignVerticalDistributeCenter=Ah,a.AlignVerticalDistributeEnd=Vh,a.AlignVerticalDistributeStart=Sh,a.AlignVerticalJustifyCenter=wh,a.AlignVerticalJustifyEnd=Lh,a.AlignVerticalJustifyStart=kh,a.AlignVerticalSpaceAround=fh,a.AlignVerticalSpaceBetween=Ph,a.Ambulance=Bh,a.Ampersand=zh,a.Ampersands=Dh,a.Amphora=Fh,a.Anchor=Rh,a.Angle=bh,a.Angry=Z1,a.Annoyed=G1,a.Antenna=Th,a.Anvil=qh,a.Aperture=Uh,a.AppWindow=Gh,a.AppWindowMac=Oh,a.Apple=Zh,a.Archive=Wh,a.ArchiveRestore=Eh,a.ArchiveX=Xh,a.AreaChart=q,a.Armchair=Ih,a.ArmenianDram=Nh,a.ArrowBigDown=Jh,a.ArrowBigDownDash=Kh,a.ArrowBigLeft=jh,a.ArrowBigLeftDash=Qh,a.ArrowBigRight=_h,a.ArrowBigRightDash=Yh,a.ArrowBigUp=at,a.ArrowBigUpDash=xh,a.ArrowDown=et,a.ArrowDown01=ht,a.ArrowDown10=tt,a.ArrowDownAZ=A,a.ArrowDownAz=A,a.ArrowDownCircle=_,a.ArrowDownFromLine=dt,a.ArrowDownLeft=ct,a.ArrowDownLeftFromCircle=x,a.ArrowDownLeftFromSquare=V0,a.ArrowDownLeftSquare=u0,a.ArrowDownNarrowWide=Mt,a.ArrowDownRight=pt,a.ArrowDownRightFromCircle=a1,a.ArrowDownRightFromSquare=S0,a.ArrowDownRightSquare=g0,a.ArrowDownSquare=A0,a.ArrowDownToDot=lt,a.ArrowDownToLine=it,a.ArrowDownUp=nt,a.ArrowDownWideNarrow=H,a.ArrowDownZA=V,a.ArrowDownZa=V,a.ArrowLeft=$t,a.ArrowLeftCircle=Y,a.ArrowLeftFromLine=rt,a.ArrowLeftRight=vt,a.ArrowLeftSquare=H0,a.ArrowLeftToLine=ot,a.ArrowRight=Ct,a.ArrowRightCircle=d1,a.ArrowRightFromLine=mt,a.ArrowRightLeft=yt,a.ArrowRightSquare=f0,a.ArrowRightToLine=st,a.ArrowUp=kt,a.ArrowUp01=gt,a.ArrowUp10=ut,a.ArrowUpAZ=S,a.ArrowUpAz=S,a.ArrowUpCircle=c1,a.ArrowUpDown=At,a.ArrowUpFromDot=Ht,a.ArrowUpFromLine=Vt,a.ArrowUpLeft=St,a.ArrowUpLeftFromCircle=h1,a.ArrowUpLeftFromSquare=w0,a.ArrowUpLeftSquare=k0,a.ArrowUpNarrowWide=w,a.ArrowUpRight=wt,a.ArrowUpRightFromCircle=t1,a.ArrowUpRightFromSquare=L0,a.ArrowUpRightSquare=P0,a.ArrowUpSquare=B0,a.ArrowUpToLine=Lt,a.ArrowUpWideNarrow=ft,a.ArrowUpZA=L,a.ArrowUpZa=L,a.ArrowsUpFromLine=Pt,a.Asterisk=zt,a.AsteriskSquare=z0,a.Astroid=Bt,a.AtSign=Dt,a.Atom=Ft,a.AudioLines=Tt,a.AudioLinesOff=Rt,a.AudioLinesX=bt,a.AudioWaveform=qt,a.Avocado=Ut,a.Award=Ot,a.Axe=Gt,a.Axis3D=f,a.Axis3d=f,a.Baby=Zt,a.Backpack=Et,a.Bacon=Wt,a.Badge=M5,a.BadgeAlert=It,a.BadgeCent=Xt,a.BadgeCheck=k,a.BadgeDollarSign=Nt,a.BadgeEuro=Kt,a.BadgeHelp=P,a.BadgeIndianRupee=Jt,a.BadgeInfo=Qt,a.BadgeJapaneseYen=jt,a.BadgeMinus=Yt,a.BadgePercent=_t,a.BadgePlus=xt,a.BadgePoundSterling=a5,a.BadgeQuestionMark=P,a.BadgeRussianRuble=h5,a.BadgeSwissFranc=t5,a.BadgeTurkishLira=d5,a.BadgeX=c5,a.BaggageClaim=p5,a.Balloon=l5,a.Ban=i5,a.Banana=n5,a.Bandage=e5,a.BangladeshiTaka=r5,a.Banknote=y5,a.BanknoteArrowDown=o5,a.BanknoteArrowUp=v5,a.BanknoteCheck=$5,a.BanknoteX=m5,a.BarChart=X,a.BarChart2=N,a.BarChart3=W,a.BarChart4=E,a.BarChartBig=Z,a.BarChartHorizontal=O,a.BarChartHorizontalBig=U,a.Barcode=s5,a.Barrel=C5,a.Baseline=u5,a.Bath=g5,a.Battery=f5,a.BatteryCharging=A5,a.BatteryFull=H5,a.BatteryLow=V5,a.BatteryMedium=S5,a.BatteryPlus=w5,a.BatteryWarning=L5,a.Beaker=k5,a.Bean=B5,a.BeanOff=P5,a.Bed=F5,a.BedDouble=z5,a.BedSingle=D5,a.Beef=b5,a.BeefOff=R5,a.Beer=q5,a.BeerOff=T5,a.BelarusianRuble=U5,a.Bell=N5,a.BellCheck=O5,a.BellDot=G5,a.BellElectric=Z5,a.BellMinus=E5,a.BellOff=W5,a.BellPlus=I5,a.BellRing=X5,a.BetweenHorizonalEnd=B,a.BetweenHorizonalStart=z,a.BetweenHorizontalEnd=B,a.BetweenHorizontalStart=z,a.BetweenVerticalEnd=K5,a.BetweenVerticalStart=J5,a.BicepsFlexed=Q5,a.Bike=j5,a.Binary=Y5,a.Binoculars=x5,a.Biohazard=_5,a.Bird=a4,a.Birdhouse=h4,a.Bitcoin=t4,a.Blend=d4,a.Blender=c4,a.Blinds=M4,a.Blocks=p4,a.Bluetooth=e4,a.BluetoothConnected=l4,a.BluetoothOff=i4,a.BluetoothSearching=n4,a.Bold=r4,a.Bolt=o4,a.Bomb=v4,a.Bone=m4,a.BoneFracture=$4,a.Book=O4,a.BookA=y4,a.BookAlert=s4,a.BookAudio=C4,a.BookBookmark=D,a.BookCheck=u4,a.BookCopy=g4,a.BookDashed=F,a.BookDown=A4,a.BookHeadphones=H4,a.BookHeart=V4,a.BookImage=S4,a.BookKey=w4,a.BookLock=L4,a.BookMarked=D,a.BookMinus=f4,a.BookOpen=B4,a.BookOpenCheck=k4,a.BookOpenText=P4,a.BookPlus=D4,a.BookSearch=z4,a.BookTemplate=F,a.BookText=F4,a.BookType=R4,a.BookUp=T4,a.BookUp2=b4,a.BookUser=q4,a.BookX=U4,a.Bookmark=X4,a.BookmarkCheck=G4,a.BookmarkMinus=Z4,a.BookmarkOff=E4,a.BookmarkPlus=W4,a.BookmarkX=I4,a.BoomBox=N4,a.Bot=Q4,a.BotMessageSquare=K4,a.BotOff=J4,a.BottleWine=j4,a.BowArrow=Y4,a.Box=_4,a.BoxSelect=X0,a.Boxes=x4,a.Braces=R,a.Brackets=a3,a.Brain=c3,a.BrainCircuit=h3,a.BrainCog=t3,a.BrainOff=d3,a.BrickWall=l3,a.BrickWallFire=M3,a.BrickWallShield=p3,a.Bridge=i3,a.Briefcase=v3,a.BriefcaseBusiness=n3,a.BriefcaseConveyorBelt=e3,a.BriefcaseMedical=r3,a.BriefcasePlus=o3,a.BringToFront=$3,a.Broccoli=m3,a.Broom=C3,a.BroomSparkles=y3,a.Brush=u3,a.BrushCleaning=s3,a.Bubbles=g3,a.Bug=H3,a.BugOff=A3,a.BugPlay=V3,a.Building=w3,a.Building2=b,a.BuildingComplex=b,a.BuildingComplexPlus=S3,a.Bus=k3,a.BusFront=L3,a.Cable=f3,a.CableCar=P3,a.Cake=z3,a.CakeSlice=B3,a.Calculator=D3,a.Calendar=hd,a.Calendar1=F3,a.CalendarArrowDown=R3,a.CalendarArrowUp=b3,a.CalendarCheck=q3,a.CalendarCheck2=T3,a.CalendarChevronsLeft=U3,a.CalendarChevronsRight=O3,a.CalendarClock=G3,a.CalendarCog=E3,a.CalendarDays=Z3,a.CalendarFold=W3,a.CalendarHeart=I3,a.CalendarMinus=N3,a.CalendarMinus2=X3,a.CalendarOff=K3,a.CalendarPlus=_3,a.CalendarPlus2=J3,a.CalendarRange=Q3,a.CalendarSearch=j3,a.CalendarSync=Y3,a.CalendarX=ad,a.CalendarX2=x3,a.Calendars=td,a.Camera=dd,a.CameraOff=cd,a.Can=pd,a.CanSoda=Md,a.CandlestickChart=G,a.Candy=nd,a.CandyCane=ld,a.CandyOff=id,a.Cannabis=rd,a.CannabisOff=ed,a.Captions=T,a.CaptionsOff=od,a.Car=yd,a.CarBattery=vd,a.CarFront=$d,a.CarTaxiFront=md,a.Caravan=sd,a.CardSim=Cd,a.Carrot=ud,a.Carton=Ad,a.CartonOff=gd,a.CaseLower=Hd,a.CaseSensitive=Vd,a.CaseUpper=Sd,a.CassetteTape=wd,a.Cast=Ld,a.Castle=fd,a.Cat=kd,a.Cctv=Bd,a.CctvOff=Pd,a.Certificate=zd,a.ChartArea=q,a.ChartBar=O,a.ChartBarBig=U,a.ChartBarDecreasing=Fd,a.ChartBarIncreasing=Dd,a.ChartBarStacked=Rd,a.ChartCandlestick=G,a.ChartColumn=W,a.ChartColumnBig=Z,a.ChartColumnDecreasing=bd,a.ChartColumnIncreasing=E,a.ChartColumnStacked=Td,a.ChartGantt=qd,a.ChartLine=I,a.ChartNetwork=Ud,a.ChartNoAxesColumn=N,a.ChartNoAxesColumnDecreasing=Od,a.ChartNoAxesColumnIncreasing=X,a.ChartNoAxesCombined=Gd,a.ChartNoAxesGantt=K,a.ChartPie=J,a.ChartScatter=Q,a.ChartSpline=Zd,a.Check=Id,a.CheckCheck=Ed,a.CheckCircle=M1,a.CheckCircle2=p1,a.CheckLine=Wd,a.CheckSquare=T0,a.CheckSquare2=q0,a.ChefHat=Xd,a.Cherry=Nd,a.ChessBishop=Jd,a.ChessKing=Kd,a.ChessKnight=Qd,a.ChessPawn=jd,a.ChessQueen=Yd,a.ChessRook=_d,a.ChevronDown=xd,a.ChevronDownCircle=l1,a.ChevronDownSquare=O0,a.ChevronFirst=a6,a.ChevronLast=h6,a.ChevronLeft=t6,a.ChevronLeftCircle=i1,a.ChevronLeftSquare=Z0,a.ChevronRight=d6,a.ChevronRightCircle=n1,a.ChevronRightSquare=U0,a.ChevronUp=c6,a.ChevronUpCircle=e1,a.ChevronUpSquare=G0,a.ChevronsDown=p6,a.ChevronsDownUp=M6,a.ChevronsLeft=n6,a.ChevronsLeftRight=i6,a.ChevronsLeftRightEllipsis=l6,a.ChevronsRight=r6,a.ChevronsRightLeft=e6,a.ChevronsUp=$6,a.ChevronsUpDown=o6,a.Church=v6,a.Cigarette=y6,a.CigaretteOff=m6,a.Circle=U6,a.CircleAlert=j,a.CircleArrowDown=_,a.CircleArrowLeft=Y,a.CircleArrowOutDownLeft=x,a.CircleArrowOutDownRight=a1,a.CircleArrowOutUpLeft=h1,a.CircleArrowOutUpRight=t1,a.CircleArrowRight=d1,a.CircleArrowUp=c1,a.CircleCheck=p1,a.CircleCheckBig=M1,a.CircleChevronDown=l1,a.CircleChevronLeft=i1,a.CircleChevronRight=n1,a.CircleChevronUp=e1,a.CircleChevronsDown=s6,a.CircleChevronsLeft=C6,a.CircleChevronsRight=u6,a.CircleChevronsUp=g6,a.CircleDashed=H6,a.CircleDashedCheck=A6,a.CircleDivide=r1,a.CircleDollarSign=V6,a.CircleDot=w6,a.CircleDotDashed=S6,a.CircleEllipsis=L6,a.CircleEqual=f6,a.CircleEuro=k6,a.CircleFadingArrowUp=P6,a.CircleFadingPlay=B6,a.CircleFadingPlus=z6,a.CircleGauge=o1,a.CircleHelp=n,a.CircleMinus=v1,a.CircleOff=D6,a.CircleParking=m1,a.CircleParkingOff=$1,a.CirclePause=y1,a.CirclePercent=s1,a.CirclePile=F6,a.CirclePlay=C1,a.CirclePlus=u1,a.CirclePoundSterling=R6,a.CirclePower=g1,a.CircleQuestionMark=n,a.CircleSlash=b6,a.CircleSlash2=A1,a.CircleSlashed=A1,a.CircleSmall=T6,a.CircleStar=q6,a.CircleStop=H1,a.CircleUser=S1,a.CircleUserRound=V1,a.CircleX=w1,a.CircuitBoard=O6,a.Citrus=G6,a.Clapperboard=Z6,a.ClefAlto=E6,a.ClefBass=W6,a.ClefTreble=I6,a.Clipboard=a8,a.ClipboardCheck=X6,a.ClipboardClock=N6,a.ClipboardCopy=K6,a.ClipboardEdit=f1,a.ClipboardList=J6,a.ClipboardMinus=Q6,a.ClipboardPaste=j6,a.ClipboardPen=f1,a.ClipboardPenLine=L1,a.ClipboardPlus=Y6,a.ClipboardSignature=L1,a.ClipboardType=_6,a.ClipboardX=x6,a.Clock=A8,a.Clock1=h8,a.Clock10=t8,a.Clock11=d8,a.Clock12=c8,a.Clock2=M8,a.Clock3=p8,a.Clock4=l8,a.Clock5=i8,a.Clock6=n8,a.Clock7=e8,a.Clock8=r8,a.Clock9=o8,a.ClockAlert=v8,a.ClockArrowDown=$8,a.ClockArrowLeft=m8,a.ClockArrowRight=y8,a.ClockArrowUp=s8,a.ClockCheck=C8,a.ClockFading=u8,a.ClockPlus=g8,a.ClosedCaption=H8,a.Cloud=E8,a.CloudAlert=V8,a.CloudBackup=S8,a.CloudCheck=w8,a.CloudCog=L8,a.CloudDownload=k1,a.CloudDrizzle=f8,a.CloudFog=k8,a.CloudHail=P8,a.CloudLightning=z8,a.CloudMoon=D8,a.CloudMoonRain=B8,a.CloudOff=F8,a.CloudRain=b8,a.CloudRainWind=R8,a.CloudSnow=T8,a.CloudSun=q8,a.CloudSunRain=U8,a.CloudSync=O8,a.CloudUpload=P1,a.Cloudy=G8,a.Clover=Z8,a.Club=W8,a.Code=I8,a.Code2=B1,a.CodeSquare=E0,a.CodeXml=B1,a.Coffee=X8,a.Cog=N8,a.Coins=K8,a.Columns=z1,a.Columns2=z1,a.Columns3=D1,a.Columns3Cog=e,a.Columns4=J8,a.ColumnsSettings=e,a.Combine=Q8,a.Command=j8,a.Compass=Y8,a.Component=_8,a.Computer=x8,a.ConciergeBell=hc,a.Cone=ac,a.Construction=tc,a.Contact=dc,a.Contact2=F1,a.ContactRound=F1,a.Container=cc,a.Contrast=Mc,a.Cookie=pc,a.CookingPot=lc,a.Copy=vc,a.CopyCheck=ic,a.CopyMinus=nc,a.CopyPlus=ec,a.CopySlash=rc,a.CopyX=oc,a.Copyleft=$c,a.Copyright=mc,a.CornerDownLeft=yc,a.CornerDownRight=sc,a.CornerLeftDown=Cc,a.CornerLeftUp=uc,a.CornerRightDown=gc,a.CornerRightUp=Hc,a.CornerUpLeft=Ac,a.CornerUpRight=Vc,a.Cpu=wc,a.CpuAi=Sc,a.CreativeCommons=Lc,a.CreditCard=Dc,a.CreditCardCheck=fc,a.CreditCardMinus=kc,a.CreditCardPlus=Pc,a.CreditCardReader=Bc,a.CreditCardX=zc,a.Croissant=Fc,a.Crop=Rc,a.Cross=bc,a.Crosshair=Tc,a.Crown=qc,a.Cuboid=Uc,a.CupSoda=Gc,a.Cupcake=Oc,a.CurlyBraces=R,a.Currency=Zc,a.Cylinder=Ec,a.Dam=Wc,a.Database=xc,a.DatabaseArrowDown=Ic,a.DatabaseArrowUp=Xc,a.DatabaseBackup=Nc,a.DatabaseCheck=Kc,a.DatabaseMinus=Qc,a.DatabasePlus=Jc,a.DatabaseSearch=jc,a.DatabaseX=Yc,a.DatabaseZap=_c,a.DecimalsArrowLeft=a7,a.DecimalsArrowRight=h7,a.Delete=d7,a.Dessert=t7,a.Diameter=c7,a.Diamond=l7,a.DiamondMinus=M7,a.DiamondPercent=R1,a.DiamondPlus=p7,a.Dice1=i7,a.Dice2=n7,a.Dice3=e7,a.Dice4=r7,a.Dice5=m7,a.Dice6=o7,a.Dices=v7,a.Diff=$7,a.Disc=u7,a.Disc2=y7,a.Disc3=s7,a.DiscAlbum=C7,a.Divide=g7,a.DivideCircle=r1,a.DivideSquare=N0,a.Dna=H7,a.DnaOff=A7,a.Dock=V7,a.Dog=S7,a.DollarSign=w7,a.Dome=L7,a.Donut=f7,a.DoorClosed=z7,a.DoorClosedCog=k7,a.DoorClosedLocked=P7,a.DoorClosedPackage=B7,a.DoorOpen=D7,a.DoorStairwell=F7,a.Dot=R7,a.DotSquare=J0,a.Download=b7,a.DownloadCloud=k1,a.DraftingCompass=T7,a.Drama=q7,a.Drill=U7,a.Drone=O7,a.Droplet=Z7,a.DropletOff=G7,a.Droplets=E7,a.Drum=W7,a.Drumstick=I7,a.Dumbbell=X7,a.Ear=K7,a.EarOff=N7,a.Earth=b1,a.EarthLock=J7,a.Eclipse=Q7,a.Edit=l,a.Edit2=c0,a.Edit3=d0,a.Egg=_7,a.EggFried=j7,a.EggOff=Y7,a.EighthNote=T1,a.EighthNotes=q1,a.Eject=x7,a.Ellipse=a9,a.Ellipsis=O1,a.EllipsisVertical=U1,a.Engine=h9,a.Equal=M9,a.EqualApproximately=d9,a.EqualApproximatelyNot=t9,a.EqualNot=c9,a.EqualSquare=K0,a.Eraser=i9,a.EthernetPort=p9,a.Euro=l9,a.EvCharger=n9,a.Expand=e9,a.ExternalLink=r9,a.Eye=m9,a.EyeClosed=o9,a.EyeDashed=v9,a.EyeOff=$9,a.FaceAngry=Z1,a.FaceExpressionless=G1,a.FaceGrinning=E1,a.FaceNeutral=W1,a.FaceSlightlyFrowning=I1,a.FaceSlightlySmiling=N1,a.FaceSlightlySmilingPlus=X1,a.Factory=y9,a.Fan=s9,a.FastForward=C9,a.Faucet=u9,a.Feather=A9,a.Fence=g9,a.FerrisWheel=H9,a.File=Y9,a.FileArchive=V9,a.FileAudio=r,a.FileAudio2=r,a.FileAxis3D=K1,a.FileAxis3d=K1,a.FileBadge=J1,a.FileBadge2=J1,a.FileBarChart=Y1,a.FileBarChart2=_1,a.FileBox=S9,a.FileBraces=j1,a.FileBracesCorner=Q1,a.FileChartColumn=_1,a.FileChartColumnIncreasing=Y1,a.FileChartLine=x1,a.FileChartPie=h2,a.FileCheck=w9,a.FileCheck2=a2,a.FileCheckCorner=a2,a.FileClock=L9,a.FileCode=f9,a.FileCode2=t2,a.FileCodeCorner=t2,a.FileCog=d2,a.FileCog2=d2,a.FileDiff=k9,a.FileDigit=P9,a.FileDown=B9,a.FileEdit=n2,a.FileExclamationPoint=c2,a.FileHeadphone=r,a.FileHeart=z9,a.FileImage=D9,a.FileInput=F9,a.FileJson=j1,a.FileJson2=Q1,a.FileKey=p2,a.FileKey2=p2,a.FileLineChart=x1,a.FileLock=M2,a.FileLock2=M2,a.FileMinus=R9,a.FileMinus2=l2,a.FileMinusCorner=l2,a.FileMusic=b9,a.FileOutput=T9,a.FilePen=n2,a.FilePenLine=i2,a.FilePieChart=h2,a.FilePlay=e2,a.FilePlus=q9,a.FilePlus2=r2,a.FilePlusCorner=r2,a.FileQuestion=o2,a.FileQuestionMark=o2,a.FileScan=U9,a.FileSearch=O9,a.FileSearch2=v2,a.FileSearchCorner=v2,a.FileSignal=$2,a.FileSignature=i2,a.FileSliders=G9,a.FileSpreadsheet=Z9,a.FileStack=E9,a.FileSymlink=W9,a.FileTerminal=I9,a.FileText=X9,a.FileType=N9,a.FileType2=m2,a.FileTypeCorner=m2,a.FileUp=K9,a.FileUser=J9,a.FileVideo=e2,a.FileVideo2=y2,a.FileVideoCamera=y2,a.FileVolume=Q9,a.FileVolume2=$2,a.FileWarning=c2,a.FileX=j9,a.FileX2=s2,a.FileXCorner=s2,a.Files=_9,a.Film=x9,a.Filter=H2,a.FilterX=A2,a.Fingerprint=C2,a.FingerprintPattern=C2,a.FireExtinguisher=aM,a.Fish=dM,a.FishOff=hM,a.FishSymbol=tM,a.FishingHook=cM,a.FishingRod=MM,a.Flag=nM,a.FlagOff=pM,a.FlagTriangleLeft=lM,a.FlagTriangleRight=iM,a.Flame=rM,a.FlameKindling=eM,a.Flashlight=vM,a.FlashlightOff=oM,a.FlaskConical=mM,a.FlaskConicalOff=$M,a.FlaskRound=yM,a.FlipHorizontal=F0,a.FlipHorizontal2=Pa,a.FlipVertical=R0,a.FlipVertical2=Ba,a.Flower=CM,a.Flower2=sM,a.Focus=uM,a.FoldHorizontal=gM,a.FoldVertical=AM,a.Folder=_M,a.FolderArchive=HM,a.FolderBookmark=VM,a.FolderCheck=SM,a.FolderClock=wM,a.FolderClosed=LM,a.FolderCode=fM,a.FolderCog=u2,a.FolderCog2=u2,a.FolderDot=kM,a.FolderDown=PM,a.FolderEdit=g2,a.FolderGit=zM,a.FolderGit2=BM,a.FolderHeart=DM,a.FolderInput=FM,a.FolderKanban=RM,a.FolderKey=bM,a.FolderLock=TM,a.FolderMinus=qM,a.FolderOpen=OM,a.FolderOpenDot=UM,a.FolderOutput=GM,a.FolderPen=g2,a.FolderPlus=ZM,a.FolderRoot=EM,a.FolderSearch=IM,a.FolderSearch2=WM,a.FolderSymlink=XM,a.FolderSync=NM,a.FolderTree=KM,a.FolderUp=JM,a.FolderUser=QM,a.FolderUsers=jM,a.FolderX=YM,a.Folders=xM,a.Footprints=ap,a.ForkKnife=Za,a.ForkKnifeCrossed=Ga,a.Forklift=hp,a.Form=tp,a.FormInput=p0,a.Forward=dp,a.Frame=cp,a.Frown=I1,a.Fuel=Mp,a.Fullscreen=pp,a.FunctionSquare=Q0,a.Funnel=H2,a.FunnelPlus=lp,a.FunnelX=A2,a.Galaxy=ip,a.GalleryHorizontal=np,a.GalleryHorizontalEnd=ep,a.GalleryThumbnails=rp,a.GalleryVertical=vp,a.GalleryVerticalEnd=op,a.Gamepad=yp,a.Gamepad2=$p,a.GamepadDirectional=mp,a.GanttChart=K,a.GanttChartSquare=m,a.GapHorizontal=sp,a.GapVertical=Cp,a.Gauge=up,a.GaugeCircle=o1,a.Gavel=gp,a.Gem=Ap,a.GeorgianLari=Vp,a.Germ=Sp,a.GermOff=Hp,a.Ghost=wp,a.Gift=Lp,a.GitBranch=Pp,a.GitBranchMinus=fp,a.GitBranchPlus=kp,a.GitCommit=V2,a.GitCommitHorizontal=V2,a.GitCommitVertical=Bp,a.GitCompare=Dp,a.GitCompareArrows=zp,a.GitFork=Fp,a.GitGraph=Rp,a.GitMerge=Tp,a.GitMergeConflict=bp,a.GitPullRequest=Ep,a.GitPullRequestArrow=qp,a.GitPullRequestClosed=Gp,a.GitPullRequestCreate=Op,a.GitPullRequestCreateArrow=Up,a.GitPullRequestDraft=Zp,a.GlassWater=Wp,a.Glasses=Ip,a.Globe=jp,a.Globe2=b1,a.GlobeCheck=Xp,a.GlobeCode=Np,a.GlobeLock=Kp,a.GlobeOff=Jp,a.GlobeX=Qp,a.Goal=Yp,a.Gpu=_p,a.Grab=P2,a.GraduationCap=xp,a.Grape=al,a.GraphHub=hl,a.GraphStar=tl,a.Grid=o,a.Grid2X2=f2,a.Grid2X2Check=S2,a.Grid2X2Plus=w2,a.Grid2X2X=L2,a.Grid2x2=f2,a.Grid2x2Check=S2,a.Grid2x2Plus=w2,a.Grid2x2X=L2,a.Grid3X3=o,a.Grid3x2=dl,a.Grid3x3=o,a.Grip=pl,a.GripHorizontal=cl,a.GripVertical=Ml,a.Groceries=ll,a.Group=il,a.Guitar=nl,a.GuitarElectric=el,a.HalfNote=k2,a.Ham=rl,a.Hamburger=ol,a.Hammer=vl,a.Hand=ul,a.HandCoins=$l,a.HandFist=ml,a.HandGrab=P2,a.HandHeart=yl,a.HandHelping=B2,a.HandMetal=sl,a.HandPlatter=Cl,a.Handbag=gl,a.HandleBottomRight=Al,a.Handshake=Hl,a.HardDrive=wl,a.HardDriveDownload=Vl,a.HardDriveUpload=Sl,a.HardHat=Ll,a.Hash=fl,a.HatGlasses=kl,a.Haze=Bl,a.Hd=Pl,a.HdmiPort=zl,a.Heading=Ul,a.Heading1=Dl,a.Heading2=Fl,a.Heading3=Rl,a.Heading4=bl,a.Heading5=Tl,a.Heading6=ql,a.HeadphoneOff=Ol,a.Headphones=Gl,a.Headset=Zl,a.Heart=Ql,a.HeartCrack=El,a.HeartHandshake=Wl,a.HeartMinus=Il,a.HeartOff=Xl,a.HeartPlus=Nl,a.HeartPulse=Kl,a.HeartX=Jl,a.Heater=jl,a.Helicopter=Yl,a.HelpCircle=n,a.HelpingHand=B2,a.Hexagon=_l,a.Highlighter=xl,a.HikingStick=ai,a.History=i0,a.Home=z2,a.Hop=hi,a.HopOff=ti,a.Hospital=di,a.Hotel=ci,a.Hourglass=pi,a.HourglassCog=Mi,a.House=z2,a.HouseCog=li,a.HouseHeart=ii,a.HousePlug=ei,a.HousePlus=ni,a.HouseWifi=ri,a.Houses=oi,a.IceCream=F2,a.IceCream2=D2,a.IceCreamBowl=D2,a.IceCreamCone=F2,a.IdCard=si,a.IdCardLanyard=vi,a.Image=Hi,a.ImageDown=$i,a.ImageMinus=mi,a.ImageOff=yi,a.ImagePlay=Ci,a.ImagePlus=ui,a.ImageUp=gi,a.ImageUpscale=Ai,a.Images=Vi,a.Import=Si,a.Inbox=fi,a.Indent=$,a.IndentDecrease=v,a.IndentIncrease=$,a.IndianRupee=wi,a.Infinity=Li,a.Info=ki,a.Inspect=ha,a.InspectionPanel=Pi,a.Italic=Bi,a.IterationCcw=zi,a.IterationCw=Fi,a.IvBag=Di,a.JapaneseYen=Ri,a.Joystick=bi,a.Kanban=qi,a.KanbanSquare=j0,a.KanbanSquareDashed=I0,a.Kayak=Ti,a.KazakhTenge=Ui,a.Key=Zi,a.KeyRound=Oi,a.KeySquare=Gi,a.Keyboard=Wi,a.KeyboardMusic=Ei,a.KeyboardOff=Ii,a.Lambda=Xi,a.Lamp=Yi,a.LampCeiling=Ni,a.LampDesk=Ki,a.LampFloor=Ji,a.LampWallDown=Qi,a.LampWallUp=ji,a.LandPlot=_i,a.Landmark=xi,a.Languages=an,a.Laptop=tn,a.Laptop2=R2,a.LaptopMinimal=R2,a.LaptopMinimalCheck=hn,a.Lasso=Mn,a.LassoSelect=dn,a.Laugh=E1,a.LayerArrowDown=cn,a.LayerArrowUp=pn,a.Layers=b2,a.Layers2=ln,a.Layers3=b2,a.LayersArrowDown=nn,a.LayersArrowUp=en,a.LayersMinus=rn,a.LayersPlus=on,a.Layout=t0,a.LayoutArrowDown=vn,a.LayoutArrowRight=$n,a.LayoutDashboard=mn,a.LayoutFreeform=yn,a.LayoutGrid=un,a.LayoutGridCircles=sn,a.LayoutList=Cn,a.LayoutPanelLeft=gn,a.LayoutPanelTop=An,a.LayoutTemplate=Hn,a.Leaf=Vn,a.LeafyGreen=Sn,a.Lectern=wn,a.Lens=kn,a.LensConcave=Ln,a.LensConvex=fn,a.LetterSpacing=Pn,a.LetterText=Va,a.Letters=Bn,a.Library=Dn,a.LibraryBig=zn,a.LibrarySquare=Y0,a.LifeBuoy=Rn,a.Ligature=Fn,a.Lightbulb=Tn,a.LightbulbOff=bn,a.Lighthouse=qn,a.LineChart=I,a.LineDotBottomVertical=Un,a.LineDotLeftHorizontal=On,a.LineDotRightHorizontal=Gn,a.LineDotTopVertical=Zn,a.LineHeight=En,a.LineSquiggle=Wn,a.LineStyle=In,a.Link=Kn,a.Link2=Nn,a.Link2Off=Xn,a.List=me,a.ListCheck=Jn,a.ListChecks=Qn,a.ListChevronsDownUp=jn,a.ListChevronsUpDown=Yn,a.ListClock=_n,a.ListCollapse=xn,a.ListEnd=ae,a.ListFilter=te,a.ListFilterPlus=he,a.ListIndentDecrease=v,a.ListIndentIncrease=$,a.ListMinus=de,a.ListMusic=ce,a.ListOrdered=Me,a.ListPlus=pe,a.ListRestart=le,a.ListSortAscending=ie,a.ListSortDescending=ee,a.ListStart=re,a.ListTodo=ne,a.ListTree=oe,a.ListVideo=ve,a.ListX=$e,a.Loader=se,a.Loader2=T2,a.LoaderCircle=T2,a.LoaderPinwheel=ye,a.Locate=ue,a.LocateFixed=Ce,a.LocateOff=ge,a.LocationEdit=G2,a.Lock=He,a.LockKeyhole=Ae,a.LockKeyholeOpen=q2,a.LockOpen=U2,a.LogIn=Ve,a.LogOut=we,a.Logs=Se,a.Lollipop=Le,a.Luggage=fe,a.MSquare=_0,a.Magnet=ke,a.Mail=Ge,a.MailBadge=Pe,a.MailCheck=Be,a.MailClock=ze,a.MailDot=De,a.MailMinus=Fe,a.MailOpen=Re,a.MailPen=be,a.MailPlus=Te,a.MailQuestion=O2,a.MailQuestionMark=O2,a.MailSearch=qe,a.MailWarning=Ue,a.MailX=Oe,a.Mailbox=Ie,a.Mails=Ze,a.Map=cr,a.MapMinus=Ee,a.MapPin=dr,a.MapPinCheck=We,a.MapPinCheckInside=Xe,a.MapPinHouse=Ne,a.MapPinMinus=Je,a.MapPinMinusInside=Ke,a.MapPinOff=je,a.MapPinPen=G2,a.MapPinPlus=_e,a.MapPinPlusInside=Qe,a.MapPinSearch=Ye,a.MapPinX=ar,a.MapPinXInside=xe,a.MapPinned=hr,a.MapPlus=tr,a.Mars=pr,a.MarsStroke=Mr,a.Martini=nr,a.Maximize=ir,a.Maximize2=lr,a.Medal=er,a.Megaphone=or,a.MegaphoneOff=rr,a.Meh=W1,a.MemoryStick=vr,a.Menu=$r,a.MenuSquare=x0,a.Merge=mr,a.MessageCircle=kr,a.MessageCircleCheck=yr,a.MessageCircleCode=sr,a.MessageCircleDashed=ur,a.MessageCircleDashedCheck=Cr,a.MessageCircleHeart=gr,a.MessageCircleMore=Ar,a.MessageCircleOff=Hr,a.MessageCirclePlus=Vr,a.MessageCircleQuestion=Z2,a.MessageCircleQuestionMark=Z2,a.MessageCircleReply=wr,a.MessageCircleText=Sr,a.MessageCircleWarning=Lr,a.MessageCircleX=fr,a.MessageSquare=Xr,a.MessageSquareCheck=Pr,a.MessageSquareCode=zr,a.MessageSquareDashed=Br,a.MessageSquareDiff=Dr,a.MessageSquareDot=Fr,a.MessageSquareHeart=Rr,a.MessageSquareLock=br,a.MessageSquareMore=Tr,a.MessageSquareOff=qr,a.MessageSquarePlus=Ur,a.MessageSquareQuote=Zr,a.MessageSquareReply=Gr,a.MessageSquareShare=Or,a.MessageSquareText=Er,a.MessageSquareWarning=Wr,a.MessageSquareX=Ir,a.MessagesCircle=Nr,a.MessagesSquare=Kr,a.Metronome=Jr,a.Mic=Yr,a.Mic2=W2,a.MicAudioLines=Qr,a.MicOff=jr,a.MicSignal=E2,a.MicVocal=W2,a.Microchip=_r,a.Microscope=xr,a.Microwave=ao,a.MidiPort=ho,a.Milestone=to,a.Milk=po,a.MilkOff=co,a.Minimize=lo,a.Minimize2=Mo,a.Minus=io,a.MinusCircle=v1,a.MinusSquare=aa,a.MirrorRectangular=no,a.MirrorRound=eo,a.Monitor=wo,a.MonitorCheck=oo,a.MonitorCloud=ro,a.MonitorCog=vo,a.MonitorDot=$o,a.MonitorDown=mo,a.MonitorOff=yo,a.MonitorPause=so,a.MonitorPc=Co,a.MonitorPlay=uo,a.MonitorSmartphone=go,a.MonitorSpeaker=Ao,a.MonitorStop=Ho,a.MonitorUp=Vo,a.MonitorX=So,a.Moon=fo,a.MoonStar=Lo,a.Mop=Po,a.MopSparkles=ko,a.MoreHorizontal=O1,a.MoreVertical=U1,a.Mosque=Bo,a.Motorbike=zo,a.Mountain=Fo,a.MountainSnow=Do,a.Mouse=Eo,a.MouseLeft=Ro,a.MouseOff=bo,a.MousePointer=Go,a.MousePointer2=qo,a.MousePointer2Off=To,a.MousePointerBan=Uo,a.MousePointerClick=Oo,a.MousePointerSquareDashed=W0,a.MouseRight=Zo,a.Mouth=Io,a.MouthOff=Wo,a.Move=tv,a.Move3D=I2,a.Move3d=I2,a.MoveDiagonal=No,a.MoveDiagonal2=Xo,a.MoveDown=Qo,a.MoveDownLeft=Ko,a.MoveDownRight=Jo,a.MoveHorizontal=jo,a.MoveLeft=Yo,a.MoveRight=_o,a.MoveUp=hv,a.MoveUpLeft=xo,a.MoveUpRight=av,a.MoveVertical=dv,a.Music=q1,a.Music2=T1,a.Music3=k2,a.Music4=m0,a.Navigation=lv,a.Navigation2=Mv,a.Navigation2Off=cv,a.NavigationOff=pv,a.NepaliRupee=iv,a.Network=nv,a.Newspaper=ev,a.Nfc=rv,a.NonBinary=ov,a.Notebook=sv,a.NotebookDot=vv,a.NotebookPen=$v,a.NotebookTabs=mv,a.NotebookText=yv,a.NotepadText=uv,a.NotepadTextDashed=Cv,a.Nut=Av,a.NutOff=gv,a.Octagon=Vv,a.OctagonAlert=X2,a.OctagonMinus=Hv,a.OctagonPause=N2,a.OctagonX=K2,a.Omega=Sv,a.Option=wv,a.Orbit=Lv,a.Origami=fv,a.Outdent=v,a.Package=bv,a.Package2=kv,a.PackageCheck=Bv,a.PackageMinus=Pv,a.PackageOpen=zv,a.PackagePlus=Dv,a.PackageSearch=Fv,a.PackageX=Rv,a.PaintBucket=Tv,a.PaintRoller=qv,a.Paintbrush=Uv,a.Paintbrush2=J2,a.PaintbrushVertical=J2,a.Palette=Ov,a.Palmtree=fa,a.Panda=Gv,a.PanelBottom=Wv,a.PanelBottomClose=Zv,a.PanelBottomDashed=Q2,a.PanelBottomInactive=Q2,a.PanelBottomOpen=Ev,a.PanelLeft=x2,a.PanelLeftClose=j2,a.PanelLeftDashed=Y2,a.PanelLeftInactive=Y2,a.PanelLeftOpen=_2,a.PanelLeftRightDashed=Iv,a.PanelRight=Jv,a.PanelRightClose=Xv,a.PanelRightDashed=a0,a.PanelRightInactive=a0,a.PanelRightOpen=Nv,a.PanelTop=Yv,a.PanelTopBottomDashed=Kv,a.PanelTopClose=Qv,a.PanelTopDashed=h0,a.PanelTopInactive=h0,a.PanelTopOpen=jv,a.PanelsLeftBottom=_v,a.PanelsLeftRight=D1,a.PanelsRightBottom=xv,a.PanelsTopBottom=e0,a.PanelsTopLeft=t0,a.PaperBag=a$,a.Paperclip=h$,a.Parasol=t$,a.Parentheses=d$,a.Park=c$,a.ParkingCircle=m1,a.ParkingCircleOff=$1,a.ParkingMeter=M$,a.ParkingSquare=da,a.ParkingSquareOff=ta,a.PartyPopper=p$,a.Pause=l$,a.PauseCircle=y1,a.PauseOctagon=N2,a.PawPrint=i$,a.PcCase=n$,a.Pen=c0,a.PenBox=l,a.PenLine=d0,a.PenOff=e$,a.PenSquare=l,a.PenTool=r$,a.Pencil=y$,a.PencilLine=o$,a.PencilOff=v$,a.PencilRuler=$$,a.PencilSparkles=m$,a.Pentagon=s$,a.Percent=C$,a.PercentCircle=s1,a.PercentDiamond=R1,a.PercentSquare=ca,a.PerfumeBottle=u$,a.PersonStanding=A$,a.Phi=g$,a.PhilippinePeso=H$,a.Phone=B$,a.PhoneCall=V$,a.PhoneForwarded=w$,a.PhoneIncoming=S$,a.PhoneLog=f$,a.PhoneMissed=L$,a.PhoneOff=k$,a.PhoneOutgoing=P$,a.Pi=z$,a.PiSquare=Ma,a.Piano=D$,a.Pickaxe=F$,a.PictureInPicture=b$,a.PictureInPicture2=R$,a.PieChart=J,a.PiggyBank=T$,a.Pilcrow=O$,a.PilcrowLeft=q$,a.PilcrowRight=U$,a.PilcrowSquare=pa,a.Pill=E$,a.PillBottle=G$,a.Pin=W$,a.PinOff=Z$,a.Pipette=N$,a.Pizza=I$,a.Plane=J$,a.PlaneLanding=X$,a.PlaneTakeoff=K$,a.PlantPot=Q$,a.Play=Y$,a.PlayCircle=C1,a.PlayOff=j$,a.PlaySquare=la,a.PlayingCard=_$,a.PlayingCards=x$,a.PlayingCardsFan=am,a.Plug=tm,a.Plug2=hm,a.PlugZap=M0,a.PlugZap2=M0,a.Plus=dm,a.PlusCircle=u1,a.PlusSquare=ia,a.PocketKnife=cm,a.Podcast=E2,a.Podium=Mm,a.Pointer=lm,a.PointerOff=pm,a.Popcorn=im,a.Popsicle=nm,a.PoundSterling=em,a.Power=om,a.PowerCircle=g1,a.PowerOff=rm,a.PowerSquare=na,a.Presentation=vm,a.Printer=ym,a.Printer3d=$m,a.PrinterCheck=mm,a.PrinterX=sm,a.Projector=um,a.Proportions=Cm,a.Puzzle=gm,a.Pyramid=Am,a.QrCode=Hm,a.Quote=Vm,a.Rabbit=Sm,a.Radar=Lm,a.Radiation=wm,a.Radical=fm,a.Radio=zm,a.RadioOff=km,a.RadioReceiver=Pm,a.RadioTower=Bm,a.Radius=Dm,a.Rainbow=Fm,a.Rat=bm,a.Ratio=Rm,a.Receipt=Xm,a.ReceiptCent=Tm,a.ReceiptEuro=qm,a.ReceiptIndianRupee=Um,a.ReceiptJapaneseYen=Om,a.ReceiptPoundSterling=Zm,a.ReceiptRussianRuble=Gm,a.ReceiptSwissFranc=Em,a.ReceiptText=Wm,a.ReceiptTurkishLira=Im,a.RectangleCircle=Nm,a.RectangleEllipsis=p0,a.RectangleGoggles=Km,a.RectangleHorizontal=Jm,a.RectangleVertical=Qm,a.Recycle=jm,a.Redo=xm,a.Redo2=Ym,a.RedoDot=_m,a.RefreshCcw=hy,a.RefreshCcwDot=ay,a.RefreshCw=dy,a.RefreshCwOff=ty,a.Refrigerator=cy,a.Regex=py,a.RemoveFormatting=My,a.Repeat=ey,a.Repeat1=iy,a.Repeat2=ly,a.RepeatOff=ny,a.Replace=oy,a.ReplaceAll=ry,a.Reply=$y,a.ReplyAll=vy,a.Rewind=my,a.Ribbon=yy,a.Road=sy,a.RobotArm=Cy,a.RobotVacuum=uy,a.Rocket=gy,a.RockingChair=Ay,a.RollerCoaster=Hy,a.Rose=Vy,a.Rotate3D=l0,a.Rotate3d=l0,a.RotateCcw=Ly,a.RotateCcwClock=i0,a.RotateCcwKey=Sy,a.RotateCcwSquare=wy,a.RotateCw=By,a.RotateCwClock=fy,a.RotateCwFadingClock=ky,a.RotateCwSquare=Py,a.Route=Dy,a.RouteOff=zy,a.Router=Fy,a.Rows=n0,a.Rows2=n0,a.Rows3=e0,a.Rows4=Ry,a.Rss=by,a.RugbyBall=Ty,a.Ruler=Uy,a.RulerDimensionLine=qy,a.RussianRuble=Oy,a.Sailboat=Ey,a.Salad=Gy,a.Sandwich=Zy,a.Satellite=Iy,a.SatelliteDish=Wy,a.SaudiRiyal=Xy,a.Save=Yy,a.SaveAll=Ny,a.SaveCheck=Ky,a.SaveOff=Jy,a.SavePen=Qy,a.SavePlus=jy,a.Scale=_y,a.Scale3D=r0,a.Scale3d=r0,a.Scaling=as,a.Scan=es,a.ScanBarcode=xy,a.ScanBox=hs,a.ScanEye=ts,a.ScanFace=ds,a.ScanHeart=cs,a.ScanLine=Ms,a.ScanQrCode=ps,a.ScanSearch=ls,a.ScanSquare=is,a.ScanText=ns,a.ScatterChart=Q,a.School=rs,a.School2=Da,a.Scissors=vs,a.ScissorsLineDashed=os,a.ScissorsSquare=ea,a.ScissorsSquareDashedBottom=D0,a.Scooter=$s,a.ScratchBlocks=ms,a.ScreenShare=ss,a.ScreenShareOff=ys,a.Screw=Cs,a.Scroll=us,a.ScrollText=gs,a.Search=fs,a.SearchAlert=As,a.SearchCheck=Hs,a.SearchCode=Vs,a.SearchSlash=Ss,a.SearchX=ws,a.Section=Ls,a.Send=Ps,a.SendHorizonal=o0,a.SendHorizontal=o0,a.SendToBack=ks,a.SeparatorHorizontal=Bs,a.SeparatorVertical=zs,a.Server=qs,a.ServerCog=Ds,a.ServerCrash=Fs,a.ServerNas=Rs,a.ServerOff=bs,a.ServerPlus=Ts,a.Settings=Os,a.Settings2=Us,a.Shapes=Gs,a.Share=Zs,a.Share2=Es,a.Sheet=Ws,a.Shell=Is,a.ShelvingUnit=Xs,a.Shield=pC,a.ShieldAlert=Ns,a.ShieldBan=Ks,a.ShieldCheck=Js,a.ShieldClose=$0,a.ShieldCog=js,a.ShieldCogCorner=Qs,a.ShieldEllipsis=Ys,a.ShieldHalf=_s,a.ShieldHouse=aC,a.ShieldKeyhole=xs,a.ShieldLock=hC,a.ShieldMinus=tC,a.ShieldOff=dC,a.ShieldPlus=cC,a.ShieldQuestion=v0,a.ShieldQuestionMark=v0,a.ShieldUser=MC,a.ShieldX=$0,a.Ship=nC,a.ShipCargo=lC,a.ShipWheel=iC,a.Shirt=eC,a.ShoppingBag=rC,a.ShoppingBasket=vC,a.ShoppingCart=mC,a.ShoppingCartMinus=oC,a.ShoppingCartPlus=$C,a.Shovel=yC,a.ShowerHead=sC,a.Shredder=CC,a.Shrimp=gC,a.ShrimpOff=uC,a.Shrink=AC,a.Shrub=HC,a.Shuffle=VC,a.Sidebar=x2,a.SidebarClose=j2,a.SidebarOpen=_2,a.Sigma=SC,a.SigmaSquare=ra,a.Signal=PC,a.SignalHigh=wC,a.SignalLow=LC,a.SignalMedium=fC,a.SignalZero=kC,a.Signature=BC,a.Signpost=DC,a.SignpostBig=zC,a.Siren=FC,a.SixteenthNotes=m0,a.SkipBack=RC,a.SkipForward=bC,a.Skull=TC,a.Slash=qC,a.SlashSquare=oa,a.Slice=UC,a.Sliders=y0,a.SlidersHorizontal=OC,a.SlidersVertical=y0,a.Smartphone=WC,a.SmartphoneCharging=GC,a.SmartphoneNfc=ZC,a.SmartphoneUserRound=EC,a.Smile=N1,a.SmilePlus=X1,a.Snail=IC,a.Snowflake=XC,a.SoapDispenserDroplet=NC,a.Sofa=KC,a.SolarPanel=JC,a.SortAsc=w,a.SortDesc=H,a.Soup=QC,a.Space=jC,a.Spade=YC,a.Sparkle=_C,a.Sparkles=s0,a.Speaker=xC,a.Speech=hu,a.SpellCheck=tu,a.SpellCheck2=au,a.Spline=cu,a.SplinePointer=du,a.Split=Mu,a.SplitSquareHorizontal=va,a.SplitSquareVertical=$a,a.Spool=pu,a.SportShoe=lu,a.Spotlight=iu,a.SprayCan=nu,a.Sprout=eu,a.Square=zu,a.SquareActivity=C0,a.SquareArrowDown=A0,a.SquareArrowDownLeft=u0,a.SquareArrowDownRight=g0,a.SquareArrowLeft=H0,a.SquareArrowOutDownLeft=V0,a.SquareArrowOutDownRight=S0,a.SquareArrowOutUpLeft=w0,a.SquareArrowOutUpRight=L0,a.SquareArrowRight=f0,a.SquareArrowRightEnter=ru,a.SquareArrowRightExit=ou,a.SquareArrowUp=B0,a.SquareArrowUpLeft=k0,a.SquareArrowUpRight=P0,a.SquareAsterisk=z0,a.SquareBookmark=b0,a.SquareBottomDashedScissors=D0,a.SquareCenterlineDashedHorizontal=F0,a.SquareCenterlineDashedVertical=R0,a.SquareChartGantt=m,a.SquareCheck=q0,a.SquareCheckBig=T0,a.SquareChevronDown=O0,a.SquareChevronLeft=Z0,a.SquareChevronRight=U0,a.SquareChevronUp=G0,a.SquareCode=E0,a.SquareDashed=X0,a.SquareDashedBottom=$u,a.SquareDashedBottomCode=vu,a.SquareDashedKanban=I0,a.SquareDashedMousePointer=W0,a.SquareDashedPlus=mu,a.SquareDashedText=y,a.SquareDashedTopSolid=yu,a.SquareDashedX=Cu,a.SquareDashedXCorner=su,a.SquareDimensions=uu,a.SquareDivide=N0,a.SquareDot=J0,a.SquareEqual=K0,a.SquareExclamationPoint=gu,a.SquareFunction=Q0,a.SquareGanttChart=m,a.SquareKanban=j0,a.SquareLibrary=Y0,a.SquareM=_0,a.SquareMenu=x0,a.SquareMinus=aa,a.SquareMousePointer=ha,a.SquareOff=Au,a.SquareParking=da,a.SquareParkingOff=ta,a.SquarePause=Hu,a.SquarePen=l,a.SquarePercent=ca,a.SquarePi=Ma,a.SquarePilcrow=pa,a.SquarePlay=la,a.SquarePlus=ia,a.SquarePower=na,a.SquareRadical=Vu,a.SquareRoundCorner=Su,a.SquareScissors=ea,a.SquareSigma=ra,a.SquareSlash=oa,a.SquareSparkles=wu,a.SquareSplitHorizontal=va,a.SquareSplitVertical=$a,a.SquareSquare=Lu,a.SquareStack=fu,a.SquareStar=ku,a.SquareStop=Pu,a.SquareTerminal=ya,a.SquareText=Bu,a.SquareUser=sa,a.SquareUserRound=ma,a.SquareX=Ca,a.SquaresExclude=Du,a.SquaresIntersect=Fu,a.SquaresSubtract=Ru,a.SquaresUnite=bu,a.Squircle=Uu,a.SquircleDashed=Tu,a.Squirrel=qu,a.Stamp=Ou,a.Star=Nu,a.StarCheck=Gu,a.StarHalf=Zu,a.StarMinus=Eu,a.StarOff=Wu,a.StarPlus=Iu,a.StarX=Xu,a.Stars=s0,a.StepBack=Ku,a.StepForward=Ju,a.Stethoscope=Qu,a.Sticker=ju,a.StickyNote=tg,a.StickyNoteCheck=Yu,a.StickyNoteMinus=_u,a.StickyNoteOff=xu,a.StickyNotePlus=ag,a.StickyNoteX=hg,a.StickyNotes=dg,a.Stone=cg,a.StopCircle=H1,a.Store=Mg,a.StretchHorizontal=pg,a.StretchVertical=lg,a.Strikethrough=ig,a.Subscript=ng,a.Subtitles=T,a.Summary=eg,a.Sun=mg,a.SunDim=rg,a.SunMedium=og,a.SunMoon=vg,a.SunSnow=$g,a.Sunrise=yg,a.Sunset=sg,a.Superscript=Cg,a.SwatchBook=ug,a.SwissFranc=gg,a.SwitchCamera=Ag,a.Sword=Hg,a.Swords=Vg,a.Syringe=Sg,a.Table=zg,a.Table2=wg,a.TableCellsMerge=Lg,a.TableCellsSplit=fg,a.TableColumnsSplit=kg,a.TableConfig=e,a.TableOfContents=Pg,a.TableProperties=Bg,a.TableRowsSplit=Dg,a.Tablet=Rg,a.TabletSmartphone=Fg,a.Tablets=Tg,a.Tag=Og,a.TagPlus=bg,a.TagX=qg,a.Tags=Ug,a.Tally1=Gg,a.Tally2=Zg,a.Tally3=Eg,a.Tally4=Ig,a.Tally5=Wg,a.Tangent=Xg,a.Target=Ng,a.Telescope=Kg,a.Tent=jg,a.TentTree=Jg,a.Terminal=Qg,a.TerminalSquare=ya,a.TestTube=Yg,a.TestTube2=ua,a.TestTubeDiagonal=ua,a.TestTubes=_g,a.Text=s,a.TextAlignCenter=ga,a.TextAlignEnd=Aa,a.TextAlignJustify=Ha,a.TextAlignJustifyCenter=xg,a.TextAlignJustifyEnd=aA,a.TextAlignJustifyStart=hA,a.TextAlignStart=s,a.TextCursor=dA,a.TextCursorInput=tA,a.TextInitial=Va,a.TextQuote=cA,a.TextSearch=MA,a.TextSelect=y,a.TextSelection=y,a.TextWrap=Sa,a.Theater=pA,a.Thermometer=nA,a.ThermometerSnowflake=lA,a.ThermometerSun=iA,a.ThumbsDown=eA,a.ThumbsUp=rA,a.TicTacToe=oA,a.Ticket=uA,a.TicketCheck=vA,a.TicketMinus=$A,a.TicketPercent=mA,a.TicketPlus=yA,a.TicketSlash=sA,a.TicketX=CA,a.Tickets=HA,a.TicketsPlane=gA,a.Timeline=AA,a.Timer=wA,a.TimerOff=VA,a.TimerReset=SA,a.ToggleLeft=LA,a.ToggleRight=fA,a.Toilet=kA,a.Tombstone=PA,a.ToolCase=BA,a.Toolbox=zA,a.Toothbrush=FA,a.ToothbrushSparkles=DA,a.Tornado=RA,a.Torus=bA,a.Touchpad=qA,a.TouchpadOff=TA,a.TowelRack=OA,a.TowerControl=UA,a.ToyBrick=GA,a.Tractor=ZA,a.TrafficCone=EA,a.Trailer=WA,a.Train=wa,a.TrainFront=XA,a.TrainFrontTunnel=IA,a.TrainTrack=NA,a.TramFront=wa,a.Transgender=KA,a.Trash=La,a.Trash2=La,a.TrashOff=JA,a.TreeDeciduous=QA,a.TreePalm=fa,a.TreePine=jA,a.Trees=YA,a.TrendingDown=_A,a.TrendingUp=aH,a.TrendingUpDown=xA,a.Triangle=dH,a.TriangleAlert=ka,a.TriangleDashed=hH,a.TriangleRight=tH,a.TrianglesCenterlineDashedHorizontal=Pa,a.TrianglesCenterlineDashedVertical=Ba,a.Trophy=cH,a.Truck=pH,a.TruckElectric=MH,a.TubeLotion=lH,a.TurkishLira=iH,a.Turntable=nH,a.Turtle=eH,a.Tv=oH,a.Tv2=za,a.TvMinimal=za,a.TvMinimalPlay=rH,a.Type=sH,a.TypeOutline=vH,a.Umbrella=mH,a.UmbrellaOff=$H,a.Underline=yH,a.Undo=gH,a.Undo2=CH,a.UndoDot=uH,a.UnfoldHorizontal=AH,a.UnfoldVertical=HH,a.Ungroup=VH,a.University=Da,a.Unlink=wH,a.Unlink2=SH,a.Unlock=U2,a.UnlockKeyhole=q2,a.Unplug=LH,a.Upload=fH,a.UploadCloud=P1,a.Usb=zH,a.UsbCPort=kH,a.User=JH,a.User2=Ua,a.UserCheck=PH,a.UserCheck2=Fa,a.UserCircle=S1,a.UserCircle2=V1,a.UserCog=BH,a.UserCog2=Ra,a.UserGroup=DH,a.UserKey=FH,a.UserLock=RH,a.UserMinus=bH,a.UserMinus2=ba,a.UserPen=qH,a.UserPlus=TH,a.UserPlus2=Ta,a.UserRound=Ua,a.UserRoundArrowLeft=UH,a.UserRoundCheck=Fa,a.UserRoundCog=Ra,a.UserRoundGroup=OH,a.UserRoundKey=GH,a.UserRoundMinus=ba,a.UserRoundPen=ZH,a.UserRoundPlus=Ta,a.UserRoundSearch=EH,a.UserRoundStar=WH,a.UserRoundX=qa,a.UserSearch=IH,a.UserShield=XH,a.UserSquare=sa,a.UserSquare2=ma,a.UserStar=NH,a.UserX=KH,a.UserX2=qa,a.Users=YH,a.Users2=Oa,a.UsersCollaboration=QH,a.UsersRound=Oa,a.UsersRoundCollaboration=jH,a.Utensils=Za,a.UtensilsCrossed=Ga,a.UtilityPole=_H,a.Van=xH,a.Variable=aV,a.Vault=hV,a.VectorPolygon=tV,a.VectorSquare=dV,a.Vegan=cV,a.VenetianMask=MV,a.Venus=lV,a.VenusAndMars=pV,a.Verified=k,a.Vibrate=nV,a.VibrateOff=iV,a.Video=rV,a.VideoOff=eV,a.Videotape=oV,a.View=vV,a.Virus=mV,a.VirusOff=$V,a.Voicemail=sV,a.Volleyball=yV,a.Volume=HV,a.Volume1=CV,a.Volume2=uV,a.VolumeOff=gV,a.VolumeX=AV,a.Vote=VV,a.Wallet=wV,a.Wallet2=Ea,a.WalletCards=SV,a.WalletMinimal=Ea,a.Wallpaper=LV,a.Wand=fV,a.Wand2=Wa,a.WandSparkles=Wa,a.Warehouse=kV,a.WashingMachine=PV,a.Watch=zV,a.Waves=Ia,a.WavesArrowDown=BV,a.WavesArrowUp=DV,a.WavesHorizontal=Ia,a.WavesLadder=FV,a.WavesVertical=RV,a.Waypoints=bV,a.Webcam=qV,a.WebcamOff=TV,a.Webhook=EV,a.WebhookOff=UV,a.Weight=GV,a.WeightTilde=OV,a.Wheat=WV,a.WheatOff=ZV,a.Whistle=IV,a.WholeWord=XV,a.Wifi=aS,a.WifiCog=NV,a.WifiHigh=KV,a.WifiLock=JV,a.WifiLow=QV,a.WifiOff=jV,a.WifiPen=YV,a.WifiSync=_V,a.WifiZero=xV,a.Wind=tS,a.WindArrowDown=dS,a.WindArrowUp=hS,a.Wine=MS,a.WineOff=cS,a.Workflow=pS,a.Worm=lS,a.WrapText=Sa,a.Wrench=nS,a.WrenchOff=iS,a.X=rS,a.XCircle=w1,a.XLineTop=eS,a.XOctagon=K2,a.XSquare=Ca,a.Zap=oS,a.ZapOff=vS,a.ZodiacAquarius=$S,a.ZodiacAries=mS,a.ZodiacCancer=yS,a.ZodiacCapricorn=sS,a.ZodiacGemini=CS,a.ZodiacLeo=uS,a.ZodiacLibra=AS,a.ZodiacOphiuchus=gS,a.ZodiacPisces=HS,a.ZodiacSagittarius=VS,a.ZodiacScorpio=SS,a.ZodiacTaurus=wS,a.ZodiacVirgo=LS,a.ZoomIn=fS,a.ZoomOut=kS,a.createElement=Ja,a.createIcons=BS,a.icons=PS}));
+//# sourceMappingURL=lucide.min.js.map
diff --git a/harborrl/webui/static/style.css b/harborrl/webui/static/style.css
new file mode 100644
index 000000000..122b593ca
--- /dev/null
+++ b/harborrl/webui/static/style.css
@@ -0,0 +1,1094 @@
+:root {
+ font-family: Inter, "Segoe UI", Arial, sans-serif;
+ color: #262a29;
+ background: #fff;
+ font-synthesis: none;
+ font-size: 14px;
+ letter-spacing: 0;
+ --green: #13745b;
+ --green-light: #edf7f2;
+ --border: #e3e7e4;
+ --muted: #727a75;
+ --red: #b83c3c;
+}
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+}
+button,
+input,
+select,
+textarea {
+ font: inherit;
+ letter-spacing: 0;
+}
+button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ border: 1px solid transparent;
+ border-radius: 6px;
+ padding: 9px 14px;
+ min-height: 38px;
+ cursor: pointer;
+ background: none;
+ color: inherit;
+ font-weight: 550;
+ transition:
+ background 0.12s,
+ border-color 0.12s;
+}
+button:hover {
+ background: #f1f4f2;
+}
+button:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
+}
+button:focus-visible,
+a:focus-visible {
+ outline: 2px solid #13745b;
+ outline-offset: 3px;
+}
+svg {
+ width: 17px;
+ height: 17px;
+ flex-shrink: 0;
+ stroke-width: 1.7;
+}
+input,
+select,
+textarea {
+ border: 1px solid #cdd5cf;
+ border-radius: 5px;
+ min-width: 0;
+ width: 100%;
+ background: #fff;
+ color: #282e2a;
+ padding: 10px 11px;
+ min-height: 40px;
+}
+input:focus,
+select:focus,
+textarea:focus {
+ outline: 2px solid #d1e9dd;
+ border-color: var(--green);
+}
+input:disabled {
+ background: #f5f7f5;
+ color: #666;
+}
+input[type="checkbox"] {
+ width: 16px;
+ height: 16px;
+ min-height: 16px;
+ accent-color: var(--green);
+}
+textarea {
+ resize: vertical;
+}
+label {
+ display: block;
+ font-size: 12px;
+ font-weight: 600;
+ margin-bottom: 7px;
+ color: #535d55;
+}
+h1,
+h2,
+h3,
+p {
+ margin: 0;
+}
+h1 {
+ font-size: 25px;
+ font-weight: 620;
+ line-height: 1.25;
+}
+h2 {
+ font-size: 16px;
+ font-weight: 620;
+}
+h3 {
+ font-size: 13px;
+ font-weight: 650;
+}
+p {
+ line-height: 1.6;
+}
+a {
+ color: var(--green);
+}
+pre {
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+button.primary {
+ background: var(--green);
+ color: #fff;
+}
+button.primary:hover {
+ background: #0f614c;
+}
+button.secondary {
+ background: #fff;
+ border-color: #d9dfda;
+}
+button.secondary:hover {
+ background: #f6f8f6;
+ border-color: #b7c4bb;
+}
+button.danger {
+ color: var(--red);
+ background: #fff5f3;
+ border-color: #efd4cf;
+}
+.icon-button {
+ width: 36px;
+ height: 36px;
+ min-height: 36px;
+ padding: 8px;
+ flex-shrink: 0;
+}
+.muted {
+ color: var(--muted);
+ font-size: 12px;
+ line-height: 1.6;
+}
+.topbar {
+ height: 70px;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid var(--border);
+ padding: 0 30px;
+ gap: 24px;
+ background: white;
+}
+.brand {
+ width: 173px;
+ display: flex;
+ flex-shrink: 0;
+}
+.brand img {
+ width: 100%;
+ height: auto;
+}
+.app-title {
+ font-size: 13px;
+ border-left: 1px solid var(--border);
+ padding-left: 24px;
+ color: #667168;
+}
+.connection {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-left: auto;
+ font-size: 12px;
+ color: #606c63;
+}
+.dot,
+.status-dot {
+ width: 7px;
+ height: 7px;
+ background: #299474;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+.connection.offline .dot {
+ background: #c4863b;
+}
+.shell {
+ display: grid;
+ grid-template-columns: 228px minmax(0, 1fr);
+ min-height: calc(100vh - 70px);
+}
+.sidebar {
+ background: #f8faf8;
+ border-right: 1px solid var(--border);
+ padding: 28px 14px 20px;
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+}
+.nav-caption {
+ font-size: 10px;
+ font-weight: 650;
+ color: #879088;
+ letter-spacing: 1.1px;
+ padding: 0 14px;
+}
+.sidebar nav {
+ margin-top: 12px;
+ display: grid;
+ gap: 5px;
+}
+.nav-item {
+ justify-content: flex-start;
+ width: 100%;
+ padding: 10px 13px;
+ color: #677068;
+ font-size: 13px;
+}
+.nav-item.active {
+ background: #eaf2ec;
+ color: #176d51;
+}
+.nav-item svg {
+ margin-right: 2px;
+}
+.count {
+ margin-left: auto;
+ font-size: 10px;
+ min-width: 20px;
+ border-radius: 4px;
+ background: #e4eae5;
+ padding: 2px 5px;
+ text-align: center;
+}
+.saved-heading {
+ margin-top: 37px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+.saved-heading .nav-caption {
+ font-size: 9px;
+ padding-right: 0;
+}
+.saved-list {
+ display: grid;
+ gap: 3px;
+ margin-top: 10px;
+}
+.saved-list > .muted {
+ padding: 0 14px;
+}
+.saved-item {
+ display: flex;
+ justify-content: flex-start;
+ min-width: 0;
+ font-size: 12px;
+ font-weight: 450;
+ padding: 9px 13px;
+ text-align: left;
+}
+.saved-item span {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.saved-item.selected {
+ color: var(--green);
+ background: #eef4ef;
+}
+.sidebar-bottom {
+ margin-top: auto;
+ padding: 40px 13px 0;
+ color: #89928b;
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ font-size: 10px;
+}
+.sidebar-bottom span {
+ overflow-wrap: anywhere;
+ line-height: 1.6;
+}
+.sidebar-bottom svg {
+ width: 14px;
+ height: 14px;
+}
+main {
+ padding: 35px 38px 40px;
+ min-width: 0;
+ max-width: 1590px;
+ width: 100%;
+ margin: 0 auto;
+}
+.page-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 18px;
+ margin-bottom: 27px;
+}
+.eyebrow {
+ color: #89938b;
+ font-size: 10px;
+ font-weight: 650;
+ letter-spacing: 1.2px;
+ margin-bottom: 9px;
+}
+.toolbar {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+.configuration-name {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ margin-bottom: 25px;
+}
+.configuration-name label {
+ margin: 0;
+ white-space: nowrap;
+}
+.configuration-name input {
+ max-width: 255px;
+ font-size: 13px;
+ height: 36px;
+ min-height: 36px;
+}
+.badge {
+ padding: 4px 7px;
+ background: #f0f2ef;
+ color: #667166;
+ font-size: 10px;
+ border-radius: 4px;
+ white-space: nowrap;
+}
+.badge.saved,
+.badge.succeeded {
+ color: #176d51;
+ background: #e6f3eb;
+}
+.badge.failed {
+ color: #b43d3d;
+ background: #fbeceb;
+}
+.badge.running {
+ color: #176d51;
+ background: #e5f4eb;
+}
+.badge.stopping,
+.badge.unknown {
+ color: #986719;
+ background: #fbf2dd;
+}
+.steps {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ border-bottom: 1px solid var(--border);
+ gap: 6px;
+ margin-bottom: 31px;
+}
+.steps button {
+ font-size: 12px;
+ border-radius: 0;
+ border-bottom: 2px solid transparent;
+ padding: 10px 2px 17px;
+ justify-content: flex-start;
+ min-width: 0;
+ color: #868c86;
+ gap: 9px;
+}
+.steps button > span {
+ height: 22px;
+ width: 22px;
+ border-radius: 50%;
+ border: 1px solid #d8ded9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ font-size: 10px;
+}
+.steps button.active {
+ border-bottom-color: var(--green);
+ color: var(--green);
+}
+.steps button.active > span {
+ background: var(--green);
+ border-color: var(--green);
+ color: white;
+}
+.editor-layout {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 242px;
+ gap: 38px;
+}
+.editor {
+ min-width: 0;
+}
+.section-heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ margin-bottom: 20px;
+}
+.form-section {
+ margin-bottom: 27px;
+}
+.form-section + .form-section {
+ border-top: 1px solid var(--border);
+ padding-top: 25px;
+}
+.form-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 20px 18px;
+}
+.field.full {
+ grid-column: 1/-1;
+}
+.field small {
+ color: #8a938c;
+ font-size: 10px;
+ display: block;
+ margin-top: 5px;
+ line-height: 1.5;
+}
+.field.required label:after {
+ content: " *";
+ color: #b9915a;
+}
+.checkbox-field {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ padding-top: 12px;
+}
+.checkbox-field label {
+ margin: 0;
+}
+.preset-options {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 9px;
+ margin-bottom: 26px;
+}
+.preset {
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ min-height: 104px;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ text-align: left;
+ padding: 14px;
+ gap: 12px;
+ font-size: 12px;
+}
+.preset svg {
+ color: #839087;
+ width: 21px;
+ height: 21px;
+}
+.preset.active {
+ border-color: #72a88c;
+ background: #f2f8f3;
+ color: #116a4e;
+}
+.preset.active svg {
+ color: #13745b;
+}
+.preset small {
+ font-size: 10px;
+ font-weight: 400;
+ color: #859085;
+ display: block;
+ margin-top: 4px;
+}
+.summary {
+ border-left: 1px solid var(--border);
+ padding-left: 25px;
+ align-self: start;
+ position: sticky;
+ top: 20px;
+ min-width: 0;
+}
+.summary-heading {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ color: #607264;
+ margin-bottom: 20px;
+}
+.summary-heading h2 {
+ font-size: 12px;
+ color: #546659;
+}
+.topology-node {
+ display: grid;
+ grid-template-columns: 30px 1fr;
+ gap: 10px;
+ align-items: center;
+ padding: 10px 0;
+}
+.topology-node > .node-icon {
+ width: 30px;
+ height: 30px;
+ background: #f0f4ef;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #617769;
+ border-radius: 5px;
+}
+.topology-node span {
+ display: block;
+ font-size: 10px;
+ color: #8a958c;
+ margin-top: 3px;
+ overflow-wrap: anywhere;
+}
+.topology-node strong {
+ font-size: 11px;
+ font-weight: 550;
+}
+.topology-connector {
+ height: 20px;
+ margin-left: 14px;
+ border-left: 1px dashed #c7d6ca;
+}
+.summary dl {
+ border-top: 1px solid var(--border);
+ margin: 21px 0 0;
+ padding: 19px 0 0;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 13px;
+ font-size: 11px;
+}
+.summary dt {
+ color: #869087;
+}
+.summary dd {
+ margin: 0;
+ text-align: right;
+ font-weight: 550;
+ overflow-wrap: anywhere;
+ max-width: 110px;
+}
+.summary-footer {
+ border-top: 1px solid var(--border);
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ margin-top: 21px;
+ padding-top: 17px;
+ font-size: 10px;
+ color: #929a93;
+}
+.summary-footer .status-dot {
+ background: #b2bbb0;
+ width: 5px;
+ height: 5px;
+}
+.editor-footer {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ padding-top: 23px;
+ border-top: 1px solid var(--border);
+ margin-top: 24px;
+}
+.editor-footer .muted {
+ margin-left: auto;
+ font-size: 10px;
+}
+.notice {
+ padding: 12px 15px;
+ margin-bottom: 22px;
+ background: #fbf5e8;
+ border: 1px solid #eadbbc;
+ border-radius: 5px;
+ color: #875e24;
+ font-size: 13px;
+ overflow-wrap: anywhere;
+}
+.notice.success {
+ background: #edf7ef;
+ border-color: #cfe5d4;
+ color: #296b42;
+}
+.validation-result {
+ margin-bottom: 24px;
+}
+.validation-row {
+ display: flex;
+ gap: 10px;
+ padding: 10px 0;
+ border-bottom: 1px solid #edf0ed;
+ font-size: 12px;
+ align-items: flex-start;
+}
+.validation-row svg {
+ width: 15px;
+ height: 15px;
+ margin-top: 2px;
+}
+.validation-row.passed svg {
+ color: var(--green);
+}
+.validation-row.failed svg {
+ color: var(--red);
+}
+.validation-row.deferred svg {
+ color: #b28943;
+}
+.validation-row strong {
+ display: block;
+ font-size: 11px;
+ font-weight: 600;
+ overflow-wrap: anywhere;
+ margin-bottom: 3px;
+}
+.validation-row p {
+ font-size: 11px;
+ color: #7c867e;
+}
+.check-status {
+ font-size: 12px;
+ font-weight: 600;
+ margin-bottom: 12px;
+ color: #5f6c63;
+}
+.file-tabs {
+ display: flex;
+ gap: 3px;
+ border: 1px solid var(--border);
+ border-bottom: 0;
+ background: #fafbf9;
+ padding: 5px 7px;
+ border-radius: 5px 5px 0 0;
+ overflow: auto;
+}
+.file-tabs button {
+ font-size: 10px;
+ min-height: 29px;
+ padding: 5px 9px;
+ white-space: nowrap;
+}
+.file-tabs button.active {
+ background: #eaf0e8;
+ color: #3e684b;
+}
+.code-panel {
+ font:
+ 11px/1.8 Consolas,
+ "SFMono-Regular",
+ monospace;
+ padding: 18px;
+ background: #fafbf9;
+ border: 1px solid var(--border);
+ margin: 0;
+ min-height: 130px;
+ max-height: 380px;
+ overflow: auto;
+ border-radius: 0 0 5px 5px;
+ color: #586758;
+}
+.preview-heading {
+ margin-bottom: 12px;
+}
+.launch-section {
+ margin-top: 26px;
+}
+.launch-section p {
+ font-size: 11px;
+ margin: 8px 0 15px;
+}
+.launch-actions {
+ display: flex;
+ gap: 9px;
+ flex-wrap: wrap;
+}
+.jobs-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 12px;
+ margin-top: 20px;
+}
+.jobs-table th {
+ text-align: left;
+ color: #8a938b;
+ font-size: 10px;
+ font-weight: 500;
+ padding: 14px 12px;
+ background: #f6f8f5;
+ border-bottom: 1px solid var(--border);
+}
+.jobs-table td {
+ padding: 16px 12px;
+ border-bottom: 1px solid var(--border);
+}
+.jobs-table tr[data-job] {
+ cursor: pointer;
+}
+.jobs-table tr[data-job]:hover,
+.jobs-table tr.selected {
+ background: #f5f9f4;
+}
+.jobs-table .job-name {
+ font-weight: 550;
+}
+.jobs-table .job-id {
+ font-size: 10px;
+ color: #90998f;
+ margin-top: 3px;
+}
+.table-wrap {
+ overflow: auto;
+}
+.empty-state {
+ padding: 70px 20px;
+ text-align: center;
+ border-block: 1px solid var(--border);
+ color: #94a18f;
+}
+.empty-state svg {
+ width: 30px;
+ height: 30px;
+ margin-bottom: 16px;
+}
+.empty-state h2 {
+ color: #60735e;
+ margin-bottom: 8px;
+}
+.empty-state p {
+ font-size: 12px;
+}
+#job-detail {
+ margin-top: 35px;
+}
+#job-meta {
+ font-size: 11px;
+ overflow-wrap: anywhere;
+ margin-top: 8px;
+}
+.log-panel {
+ background: #1e2824;
+ color: #c3d3c8;
+ border-color: #1e2824;
+ border-radius: 5px;
+ min-height: 260px;
+ max-height: 510px;
+ font-size: 11px;
+}
+#log-note {
+ font-size: 10px;
+ margin-top: 10px;
+}
+dialog {
+ border: 1px solid #dce2da;
+ border-radius: 8px;
+ padding: 26px;
+ width: min(540px, calc(100% - 32px));
+ box-shadow: 0 20px 90px #15291d25;
+ color: #354237;
+}
+dialog::backdrop {
+ background: #172a1c55;
+}
+dialog p {
+ font-size: 13px;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ margin-top: 18px;
+}
+.dialog-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+}
+.dialog-actions {
+ display: flex;
+ gap: 10px;
+ justify-content: flex-end;
+ margin-top: 25px;
+}
+#answers-dialog {
+ width: min(850px, calc(100% - 32px));
+}
+#answers-editor {
+ font:
+ 12px/1.6 Consolas,
+ monospace;
+ height: 50vh;
+ margin-top: 18px;
+}
+.worker-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 20px 0 12px;
+}
+.worker-group {
+ border-bottom: 1px solid var(--border);
+ padding-bottom: 20px;
+}
+.error-field {
+ border-color: #bf7770;
+}
+.small-select {
+ max-width: 130px;
+}
+.visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+}
+[hidden] {
+ display: none !important;
+}
+.mobile-only {
+ display: none;
+}
+.nav-caption,
+.eyebrow {
+ letter-spacing: 0;
+}
+.log-selector {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-top: 22px;
+}
+.log-selector label {
+ margin: 0;
+}
+@media (min-width: 1600px) {
+ main {
+ padding-left: 55px;
+ padding-right: 55px;
+ }
+ .editor-layout {
+ grid-template-columns: minmax(0, 1fr) 280px;
+ gap: 50px;
+ }
+ .summary {
+ padding-left: 32px;
+ }
+}
+@media (max-width: 1150px) {
+ .shell {
+ grid-template-columns: 195px minmax(0, 1fr);
+ }
+ main {
+ padding: 28px 24px;
+ }
+ .editor-layout {
+ grid-template-columns: minmax(0, 1fr) 195px;
+ gap: 23px;
+ }
+ .summary {
+ padding-left: 19px;
+ }
+ .preset {
+ padding: 11px;
+ }
+ .steps button {
+ font-size: 10px;
+ gap: 6px;
+ }
+ .saved-heading .nav-caption {
+ font-size: 8px;
+ padding-left: 10px;
+ }
+}
+@media (max-width: 900px) {
+ .editor-layout {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .summary {
+ display: none;
+ }
+ .shell {
+ grid-template-columns: 185px minmax(0, 1fr);
+ }
+ .page-heading .secondary {
+ font-size: 11px;
+ padding: 7px 9px;
+ }
+ .configuration-name {
+ gap: 10px;
+ }
+ .configuration-name label {
+ font-size: 10px;
+ }
+}
+@media (max-width: 650px) {
+ .mobile-only {
+ display: inline-flex;
+ }
+ .topbar {
+ height: 61px;
+ padding: 0 17px;
+ gap: 14px;
+ }
+ .brand {
+ width: 139px;
+ }
+ .app-title {
+ display: none;
+ }
+ .connection {
+ font-size: 10px;
+ }
+ .shell {
+ display: block;
+ min-height: calc(100vh - 61px);
+ }
+ .sidebar {
+ padding: 10px 14px;
+ border-right: 0;
+ border-bottom: 1px solid var(--border);
+ display: block;
+ }
+ .sidebar > .nav-caption,
+ .saved-heading,
+ .sidebar-bottom {
+ display: none;
+ }
+ .sidebar nav {
+ margin: 0;
+ display: flex;
+ gap: 10px;
+ }
+ .nav-item {
+ width: auto;
+ padding: 6px 12px;
+ font-size: 12px;
+ min-height: 34px;
+ }
+ .saved-list {
+ display: flex;
+ overflow: auto;
+ margin-top: 8px;
+ gap: 5px;
+ }
+ .saved-item {
+ padding: 5px 9px;
+ min-height: 30px;
+ max-width: 200px;
+ flex-shrink: 0;
+ }
+ .saved-list > .muted {
+ font-size: 10px;
+ padding-left: 12px;
+ }
+ main {
+ padding: 25px 18px;
+ }
+ .page-heading {
+ align-items: flex-start;
+ margin-bottom: 24px;
+ gap: 10px;
+ }
+ h1 {
+ font-size: 21px;
+ }
+ .eyebrow {
+ font-size: 9px;
+ }
+ .page-heading .secondary {
+ font-size: 0;
+ gap: 0;
+ width: 36px;
+ padding: 8px;
+ }
+ .page-heading .toolbar {
+ gap: 2px;
+ }
+ .configuration-name {
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+ .configuration-name label {
+ width: 100%;
+ font-size: 11px;
+ }
+ .configuration-name input {
+ max-width: calc(100% - 65px);
+ }
+ .steps {
+ gap: 3px;
+ margin-bottom: 25px;
+ }
+ .steps button {
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ font-size: 10px;
+ gap: 8px;
+ text-align: center;
+ line-height: 1.4;
+ padding: 8px 0 12px;
+ }
+ .steps button > span {
+ width: 20px;
+ height: 20px;
+ }
+ .form-grid {
+ grid-template-columns: minmax(0, 1fr);
+ gap: 17px;
+ }
+ .preset-options {
+ gap: 6px;
+ }
+ .preset {
+ min-height: 110px;
+ font-size: 10px;
+ gap: 8px;
+ padding: 10px 7px;
+ }
+ .preset small {
+ font-size: 9px;
+ line-height: 1.5;
+ }
+ .preset svg {
+ height: 18px;
+ width: 18px;
+ }
+ .section-heading h2 {
+ font-size: 14px;
+ }
+ .section-heading .secondary {
+ font-size: 10px;
+ padding: 7px 9px;
+ }
+ .editor-footer {
+ gap: 8px;
+ }
+ .editor-footer button {
+ font-size: 11px;
+ padding: 8px 10px;
+ }
+ .editor-footer .muted {
+ font-size: 9px;
+ }
+ .launch-actions {
+ gap: 7px;
+ }
+ .launch-actions button {
+ font-size: 11px;
+ padding: 8px 10px;
+ }
+ .jobs-table th,
+ .jobs-table td {
+ padding: 12px 8px;
+ }
+ .jobs-table th:nth-child(3),
+ .jobs-table td:nth-child(3) {
+ display: none;
+ }
+ .jobs-table {
+ font-size: 11px;
+ }
+ .code-panel {
+ font-size: 10px;
+ }
+ .dialog-actions {
+ flex-wrap: wrap;
+ }
+}
diff --git a/practices/h_cluster/README.md b/practices/h_cluster/README.md
new file mode 100644
index 000000000..36fcd7cd3
--- /dev/null
+++ b/practices/h_cluster/README.md
@@ -0,0 +1,100 @@
+# H-cluster deployment practice
+
+This optional package adapts HarborRL to Brainbox and restricted Sandbox egress.
+It shares the core Controller, scheduling, Harbor Runner and trajectory audit;
+it does not implement another training loop. Installing HarborRL alone does not
+install or import this package. All example addresses and paths are placeholders.
+
+## Topology
+
+```text
+GPU training adapter -> developer Controller / RolloutService
+ -> local Harbor Runner -> Sandbox platform
+Sandbox Claude -> loopback mailbox <- developer relay pump
+ -> developer Gateway -> GPU SGLang
+```
+
+The developer initiates authenticated platform-proxy requests. Sandbox needs
+no direct route to the GPU or developer private listener. The developer must
+reach the platform proxy and every advertised inference endpoint; the GPU
+must reach the controller. Relay responses, including SSE, are fully buffered.
+
+## Install and configure
+
+Use Python 3.12+ for the runner. From the repository root:
+
+```bash
+python -m pip install -e . -e practices/h_cluster
+```
+
+Install the practice in the runner environment. The controller can use a separate
+Python environment with the dependencies required for its tokenizer and service.
+Start with `configs/deployment.example.yaml` and `configs/sandbox.example.yaml`.
+Use service placement, a local developer runner and no SSH rollout workers.
+Set the training config's `harbor.environment.runtime` to `sandbox`, its
+`config` to your Sandbox config, and `harbor.runner_transport` to `local`.
+
+For each task image create a matching platform Environment. Expose command
+port 44772 and the configured relay port. Its capacity must cover the binding
+limit; use prewarm 0 for an idle test deployment. Pin images and offline Claude
+binary hashes. The all-zero example hash must be replaced. Put AK:SK credentials
+in the referenced mode-600 file; never commit credentials or private site files.
+Tasks and verifiers needing external packages must have those dependencies
+prepared offline. The model relay is not a general internet proxy.
+
+The core only interprets common lifecycle settings. This package validates
+project, Environment IDs, mounts and relay options. Whole-Gi memory values are
+expressed using Gi. Endpoint GET 502/503/504 responses are retried within the
+creation deadline; the allocation POST is never retried. Platform Pending can
+still time out and must not be mistaken for ready capacity.
+
+## Verify and run
+
+No GPU or platform credentials are needed for the package's local HTTP/mock tests:
+
+```bash
+python -m pip install -e '.[dev]' -e practices/h_cluster
+python -m pytest practices/h_cluster/tests
+python scripts/sandbox_sdk_contract.py
+```
+
+After filling in private configuration, use the runner interpreter:
+
+```bash
+python -m harborrl.rollout.harbor_job.sandbox_probe /srv/harborrl/site/sandbox.yaml
+python -m harborrl.rollout.harbor_job.sandbox_probe /srv/harborrl/site/sandbox.yaml --network
+harborrl train /srv/harborrl/site/train.yaml --deployment /srv/harborrl/site/deployment.yaml --dry-run
+```
+
+The first command is local. The second performs authenticated, read-only
+Environment checks and does not create instances. The dry run parses the launch
+plan and does not contact the GPU. Check actual routes before starting training.
+Removing `--dry-run` launches the selected GPU job and can allocate Sandbox
+instances. Use a new run directory for each test.
+
+Inspect instance lifecycle files and cleanup-required records after cancellation.
+An empty instance list can disagree with Environment capacity details; verify
+both before reusing capacity. Never delete templates or instances belonging to
+other runs. The framework records unresolved cleanup rather than assuming it
+succeeded. It does not automatically replace platform Environment templates.
+
+## Configuration migration
+
+Experimental flat Brainbox configs are intentionally not accepted as standard
+OpenSandbox settings. Replace `provider: brainbox` with
+`adapter: harborrl_h_cluster.environment:BrainboxEnvironment`; move project and
+relay settings under `options`, and Environment ID/mount requirements under
+each binding's `options`. The platform always uses command port 44772.
+Keep lifecycle limits and offline installation in their common top-level fields.
+
+## Operational limits
+
+Set concurrency within project quotas and each Environment's actual capacity.
+Measure agent execution separately from allocated or Running instances.
+Pending allocations, offline verifier dependencies, turn limits and invalid
+generated tools can reject a complete training group. Session content must
+match the recorded model response to pass consumption auditing.
+
+Start with a serial complete-group run before increasing concurrency. Inspect
+rejected groups and confirm cleanup of every allocated instance. Checkpoint
+creation and policy-version changes alone do not establish learning improvement.
diff --git a/practices/h_cluster/configs/deployment.example.yaml b/practices/h_cluster/configs/deployment.example.yaml
new file mode 100644
index 000000000..e89c2b62f
--- /dev/null
+++ b/practices/h_cluster/configs/deployment.example.yaml
@@ -0,0 +1,37 @@
+# Replace example hosts and paths; no site-specific addresses are required.
+schema_version: 1
+manager:
+ id: manager
+ mode: service
+ bind: 0.0.0.0
+ control_port: 49200
+ control_url: http://manager.example:49200
+ gateway_port: 49184
+ gateway_url: http://manager.example:49184
+ run_token_file: /srv/harborrl/site/run-token
+ source_dir: /srv/harborrl/repo
+ controller_python: /opt/harborrl/controller/bin/python
+gpu_target:
+ id: trainer
+ transport: ssh
+ ssh: {host: training-host}
+ source_dir: /srv/harborrl/repo
+ train_python: /opt/harborrl/training/bin/python
+ environment_file: /srv/harborrl/site/gpu-env.json
+ expected_gpus: 8
+storage:
+ mode: shared_fs
+ run_root: /srv/harborrl/runs
+ manager_writable: true
+ gpu_writable: true
+ worker_writable: true
+rollout:
+ provider: sandbox
+ workers: []
+connectivity:
+ command_timeout_s: 30
+ ssh_timeout_s: 15
+ http_timeout_s: 10
+ gpu_to_controller: true
+ worker_to_gateway: false
+ manager_to_gpu_inference: true
diff --git a/practices/h_cluster/configs/sandbox.example.yaml b/practices/h_cluster/configs/sandbox.example.yaml
new file mode 100644
index 000000000..6229e8659
--- /dev/null
+++ b/practices/h_cluster/configs/sandbox.example.yaml
@@ -0,0 +1,23 @@
+adapter: harborrl_h_cluster.environment:BrainboxEnvironment
+domain: https://sandbox.example/brainbox
+credential_file: /run/secrets/harborrl/sandbox-key
+max_live_instances: 8
+startup_concurrency: 1
+request_timeout_s: 180
+create_timeout_s: 600
+options:
+ project: REPLACE_PROJECT
+ gateway_transport: platform_pull
+ relay_port: 49185
+ relay_timeout_s: 180
+bindings:
+ - image: registry.example/project/task:locked-version
+ max_live_instances: 1
+ options:
+ environment_id: REPLACE_ENVIRONMENT_ID
+ required_mount_paths: []
+installation:
+ mode: offline_binary
+ version: 2.1.141
+ binary: /opt/harborrl/assets/claude
+ sha256: '0000000000000000000000000000000000000000000000000000000000000000'
diff --git a/practices/h_cluster/pyproject.toml b/practices/h_cluster/pyproject.toml
new file mode 100644
index 000000000..0e06e0611
--- /dev/null
+++ b/practices/h_cluster/pyproject.toml
@@ -0,0 +1,16 @@
+[build-system]
+requires = ["setuptools>=69", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "harborrl-h-cluster"
+version = "0.1.0"
+description = "Optional H-cluster deployment practice for HarborRL"
+requires-python = ">=3.12"
+dependencies = ["harborrl>=0.1.0", "harbor==0.23.0", "opensandbox==0.1.9", "httpx>=0.27,<1"]
+
+[tool.setuptools.packages.find]
+where = ["src"]
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
diff --git a/practices/h_cluster/src/harborrl_h_cluster/__init__.py b/practices/h_cluster/src/harborrl_h_cluster/__init__.py
new file mode 100644
index 000000000..e8e8a73b2
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/__init__.py
@@ -0,0 +1 @@
+"""Optional H-cluster deployment adapters; not imported by HarborRL core."""
diff --git a/practices/h_cluster/src/harborrl_h_cluster/environment.py b/practices/h_cluster/src/harborrl_h_cluster/environment.py
new file mode 100644
index 000000000..6b0b68527
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/environment.py
@@ -0,0 +1,143 @@
+"""Brainbox-specific settings and relay on the shared audited lifecycle."""
+import asyncio
+import re
+from contextlib import suppress
+
+import httpx
+
+from harborrl.rollout.harbor_job.environments.opensandbox import (
+ ManagedOpenSandboxEnvironment,
+)
+from harborrl.rollout.harbor_job.sandbox_config import read_credential
+from harborrl.trajectories.native import publish
+from harborrl_h_cluster.transport import SandboxStartupTransport
+
+
+async def validate_environment(cfg, binding, credential):
+ options, target = cfg['options'], binding['options']
+ async with httpx.AsyncClient(timeout=cfg['request_timeout_s']) as client:
+ response = await client.get(
+ cfg['domain'].rstrip('/') + '/v1/sandbox-environments/' + target['environment_id'],
+ params={'project': options['project']}, headers={'OPEN-SANDBOX-API-KEY': credential})
+ if response.status_code != 200:
+ raise RuntimeError(f'Environment query failed: HTTP {response.status_code}')
+ environment = response.json()
+ if environment.get('image', {}).get('uri') != binding['image']:
+ raise ValueError('Environment image does not match task image')
+ required_ports = {44772}
+ if options['gateway_transport'] == 'platform_pull':
+ required_ports.add(options['relay_port'])
+ if not required_ports <= {p.get('containerPort') for p in environment.get('ports', [])}:
+ raise ValueError('Environment command or relay port missing')
+ capacity = environment.get('instanceCapacity')
+ if type(capacity) is not int or capacity < binding['max_live_instances']:
+ raise ValueError('Environment capacity below configured admission limit')
+ mounts = {v.get('mountPath') for v in environment.get('volumes', [])}
+ if set(target['required_mount_paths']) - mounts:
+ raise ValueError('Environment required mounts missing')
+ return {'environment_id': target['environment_id'], 'image': binding['image'], 'capacity': capacity}
+
+
+class BrainboxEnvironment(ManagedOpenSandboxEnvironment):
+ @classmethod
+ def validate_config(cls, cfg):
+ options = cfg['options']
+ if set(options) != {'project', 'gateway_transport', 'relay_port', 'relay_timeout_s'}:
+ raise ValueError('H-cluster options require project, gateway_transport, relay_port, relay_timeout_s')
+ if not isinstance(options['project'], str) or not options['project'].strip():
+ raise ValueError('H-cluster project required')
+ if options['gateway_transport'] not in ('direct', 'platform_pull') or cfg['use_server_proxy'] is not True:
+ raise ValueError('H-cluster requires server proxy and a supported gateway transport')
+ if (type(options['relay_port']) is not int or not 0 < options['relay_port'] < 65536
+ or options['relay_port'] == 44772 or type(options['relay_timeout_s']) is not int
+ or options['relay_timeout_s'] < 30):
+ raise ValueError('invalid H-cluster relay port/timeout')
+ seen = set()
+ for binding in cfg['bindings']:
+ target = binding['options']
+ if set(target) != {'environment_id', 'required_mount_paths'}:
+ raise ValueError('H-cluster binding requires environment_id and required_mount_paths')
+ eid = target['environment_id']
+ if not isinstance(eid, str) or not re.fullmatch(r'[A-Za-z0-9_-]+', eid) or eid in seen:
+ raise ValueError('invalid or duplicate H-cluster Environment ID')
+ seen.add(eid)
+ if not isinstance(target['required_mount_paths'], list) or any(
+ not isinstance(p, str) or not p.startswith('/') for p in target['required_mount_paths']):
+ raise ValueError('invalid required_mount_paths')
+
+ @classmethod
+ def agent_gateway(cls, cfg, gateway_url):
+ if cfg['options']['gateway_transport'] == 'platform_pull':
+ return f"http://127.0.0.1:{cfg['options']['relay_port']}"
+ return gateway_url
+
+ @classmethod
+ async def network_check(cls, cfg):
+ credential = read_credential(cfg)
+ if ':' not in credential:
+ raise ValueError('H-cluster credential must contain AK:SK')
+ return [await validate_environment(cfg, binding, credential) for binding in cfg['bindings']]
+
+ def __init__(self, *args, **kwargs):
+ self.relay_task = None
+ super().__init__(*args, **kwargs)
+ self.record['environment_id'] = self.binding['options']['environment_id']
+
+ async def validate_binding(self):
+ if ':' not in self._api_key:
+ raise ValueError('H-cluster credential must contain AK:SK')
+ await validate_environment(self.cfg, self.binding, self._api_key)
+
+ def extensions(self):
+ return {'environmentId': self.binding['options']['environment_id']}
+
+ def api_params(self):
+ return {'project': self.cfg['options']['project']}
+
+ def startup_transport(self, allocated):
+ return SandboxStartupTransport(self.cfg['domain'].rstrip('/') + '/v1', allocated,
+ timeout=self.cfg['create_timeout_s'])
+
+ def _build_resource(self):
+ resource = super()._build_resource()
+ memory = re.fullmatch(r'(\d+)Mi', resource.get('memory', ''))
+ if memory and int(memory[1]) % 1024 == 0:
+ resource['memory'] = f'{int(memory[1]) // 1024}Gi'
+ return resource
+
+ async def start(self, force_build):
+ try:
+ await super().start(force_build)
+ if self.cfg['options']['gateway_transport'] == 'platform_pull':
+ from harborrl_h_cluster.relay import start_relay
+ if not self.gateway_url:
+ raise ValueError('platform relay requires manager Gateway URL')
+ self.relay_task = await start_relay(self, self.gateway_url)
+ except BaseException:
+ if self._sandbox is not None:
+ await self.stop(delete=True)
+ raise
+
+ async def exec(self, *args, **kwargs):
+ if self.relay_task is None:
+ return await super().exec(*args, **kwargs)
+ command = asyncio.create_task(super().exec(*args, **kwargs))
+ try:
+ done, _ = await asyncio.wait((command, self.relay_task), return_when=asyncio.FIRST_COMPLETED)
+ if self.relay_task in done:
+ publish(self.evidence / 'relay-failed.json', {'reason': 'manager relay pump stopped'})
+ raise RuntimeError('manager relay pump stopped; attempt invalid')
+ return await command
+ finally:
+ if not command.done():
+ command.cancel()
+ with suppress(asyncio.CancelledError):
+ await command
+
+ async def stop(self, delete=True):
+ if self.relay_task is not None:
+ task, self.relay_task = self.relay_task, None
+ task.cancel()
+ with suppress(asyncio.CancelledError, Exception):
+ await task
+ await super().stop(delete=delete)
diff --git a/practices/h_cluster/src/harborrl_h_cluster/relay.py b/practices/h_cluster/src/harborrl_h_cluster/relay.py
new file mode 100644
index 000000000..3bf7d5b32
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/relay.py
@@ -0,0 +1,50 @@
+"""Lifecycle of one manager-initiated relay inside a real sandbox."""
+import asyncio
+import secrets
+import tempfile
+from pathlib import Path
+
+import httpx
+
+from harborrl.trajectories.native import publish
+from harborrl_h_cluster import relay_server
+from harborrl_h_cluster.reverse_relay import pump
+
+
+async def start_relay(environment, gateway_url):
+ cfg = {**environment.cfg, **environment.cfg['options']}
+ token = secrets.token_hex(32)
+ await environment.upload_file(Path(relay_server.__file__), '/tmp/harborrl-relay.py')
+ with tempfile.NamedTemporaryFile(mode='w', encoding='ascii') as secret:
+ secret.write(token)
+ secret.flush()
+ await environment.upload_file(Path(secret.name), '/tmp/harborrl-relay-token')
+ result = await environment.exec('chmod 600 /tmp/harborrl-relay-token && '
+ 'nohup python3 /tmp/harborrl-relay.py --token-file /tmp/harborrl-relay-token '
+ f"--port {cfg['relay_port']} --timeout {cfg['relay_timeout_s']} "
+ '> /tmp/harborrl-relay.log 2>&1 < /dev/null &')
+ if result.return_code:
+ raise RuntimeError('sandbox relay process failed to start')
+ endpoint = await environment._sandbox.get_endpoint(cfg['relay_port'])
+ url = endpoint.endpoint
+ if not url.startswith(('http://', 'https://')):
+ url = cfg['protocol'] + '://' + url
+ headers = endpoint.headers or {}
+ async with httpx.AsyncClient(timeout=10, trust_env=False) as client:
+ for _ in range(20):
+ try:
+ response = await client.get(url.rstrip('/') + '/relay/health',
+ headers={**headers, 'X-Relay-Token': token})
+ if response.status_code == 200 and response.json() == {'ok': True}:
+ break
+ except (httpx.HTTPError, ValueError):
+ pass
+ await asyncio.sleep(1)
+ else:
+ raise RuntimeError('sandbox relay readiness failed')
+ publish(environment.evidence / 'relay-ready.json', {
+ 'transport': 'platform_pull', 'port': cfg['relay_port'],
+ 'upstream': gateway_url, 'sandbox_id': environment.record['sandbox_id'],
+ 'response_mode': 'buffered', 'timeout_s': cfg['relay_timeout_s']})
+ return asyncio.create_task(pump(url, headers, token, gateway_url, asyncio.Event(),
+ timeout=cfg['relay_timeout_s'] - 10))
diff --git a/practices/h_cluster/src/harborrl_h_cluster/relay_server.py b/practices/h_cluster/src/harborrl_h_cluster/relay_server.py
new file mode 100644
index 000000000..689d47c1c
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/relay_server.py
@@ -0,0 +1,150 @@
+"""Sandbox-local HTTP mailbox. Standalone stdlib file, uploaded by the runner.
+
+The manager pulls each request once. Upstream inference is never retried after
+an ambiguous transport failure. This prototype buffers responses, including SSE.
+"""
+import argparse
+import base64
+import hmac
+import json
+import queue
+import secrets
+import threading
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+
+MAX_BODY = 16 * 1024 * 1024
+PATHS = {'/v1/messages', '/v1/messages/count_tokens'}
+REQUEST_HEADERS = {'content-type', 'x-api-key', 'authorization', 'anthropic-version', 'anthropic-beta'}
+RESPONSE_HEADERS = {'content-type', 'request-id', 'retry-after'}
+
+
+class Mailbox:
+ def __init__(self, token, *, timeout=120, capacity=8):
+ self.token, self.timeout = token, timeout
+ self.slots = threading.BoundedSemaphore(capacity)
+ self.queue = queue.Queue(maxsize=capacity)
+ self.pending = {}
+ self.lock = threading.Lock()
+
+
+def make_server(host, port, mailbox):
+ class Handler(BaseHTTPRequestHandler):
+ def log_message(self, *args):
+ pass
+
+ def reply(self, status, body, headers=None):
+ self.send_response(status)
+ for key, value in (headers or {}).items():
+ if key.lower() in RESPONSE_HEADERS:
+ self.send_header(key, value)
+ self.send_header('Content-Length', str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ def json_reply(self, status, data):
+ self.reply(status, json.dumps(data).encode(), {'content-type': 'application/json'})
+
+ def authorized(self):
+ return hmac.compare_digest(self.headers.get('X-Relay-Token', ''), mailbox.token)
+
+ def read_body(self):
+ length = int(self.headers.get('Content-Length', '0'))
+ if not 0 <= length <= MAX_BODY or self.headers.get('Transfer-Encoding'):
+ raise ValueError('invalid body length')
+ return self.rfile.read(length)
+
+ def do_GET(self):
+ if not self.authorized():
+ self.json_reply(403, {'error': 'forbidden'})
+ return
+ if self.path == '/relay/health':
+ self.json_reply(200, {'ok': True})
+ return
+ if self.path != '/relay/next':
+ self.json_reply(404, {'error': 'not_found'})
+ return
+ while True:
+ try:
+ item = mailbox.queue.get_nowait()
+ except queue.Empty:
+ self.reply(204, b'')
+ return
+ with mailbox.lock:
+ active = item['id'] in mailbox.pending
+ if active:
+ self.json_reply(200, item)
+ return
+
+ def do_POST(self):
+ try:
+ self.handle_post()
+ except (ValueError, KeyError, TypeError):
+ self.json_reply(400, {'error': 'invalid_request'})
+
+ def handle_post(self):
+ if self.path.startswith('/relay/result/'):
+ if not self.authorized():
+ self.json_reply(403, {'error': 'forbidden'})
+ return
+ data = json.loads(self.read_body())
+ if type(data['status']) is not int or not 200 <= data['status'] <= 599:
+ raise ValueError('invalid status')
+ body = base64.b64decode(data['body'], validate=True)
+ headers = data['headers']
+ if not isinstance(headers, dict) or any(
+ not isinstance(k, str) or not isinstance(v, str) or '\r' in v or '\n' in v
+ for k, v in headers.items()
+ ):
+ raise ValueError('invalid response headers')
+ with mailbox.lock:
+ pending = mailbox.pending.get(self.path.rsplit('/', 1)[-1])
+ if pending is None or pending['event'].is_set():
+ self.json_reply(409, {'error': 'request_expired_or_completed'})
+ return
+ pending['result'] = (data['status'], body, headers)
+ pending['event'].set()
+ self.json_reply(200, {'ok': True})
+ return
+ if self.path.split('?', 1)[0] not in PATHS:
+ self.json_reply(404, {'error': 'not_found'})
+ return
+ body = self.read_body()
+ if not mailbox.slots.acquire(blocking=False):
+ self.json_reply(503, {'error': 'relay_capacity'})
+ return
+ identity = secrets.token_hex(16)
+ pending = {'event': threading.Event()}
+ try:
+ with mailbox.lock:
+ mailbox.pending[identity] = pending
+ try:
+ mailbox.queue.put_nowait({'id': identity, 'path': self.path, 'method': 'POST',
+ 'headers': {k: v for k, v in self.headers.items() if k.lower() in REQUEST_HEADERS},
+ 'body': base64.b64encode(body).decode()})
+ except queue.Full:
+ self.json_reply(503, {'error': 'relay_queue_full'})
+ return
+ if not pending['event'].wait(mailbox.timeout):
+ self.json_reply(504, {'error': 'relay_deadline'})
+ return
+ self.reply(*pending['result'])
+ finally:
+ with mailbox.lock:
+ mailbox.pending.pop(identity, None)
+ mailbox.slots.release()
+
+ server = ThreadingHTTPServer((host, port), Handler)
+ server.daemon_threads = True
+ return server
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--token-file', required=True)
+ parser.add_argument('--port', type=int, default=49185)
+ parser.add_argument('--timeout', type=float, default=120)
+ args = parser.parse_args()
+ server = make_server('0.0.0.0', args.port,
+ Mailbox(Path(args.token_file).read_text().strip(), timeout=args.timeout))
+ server.serve_forever()
diff --git a/practices/h_cluster/src/harborrl_h_cluster/reverse_relay.py b/practices/h_cluster/src/harborrl_h_cluster/reverse_relay.py
new file mode 100644
index 000000000..86316dbac
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/reverse_relay.py
@@ -0,0 +1,49 @@
+"""Manager-initiated sandbox relay over the authenticated platform proxy.
+
+Experimental transport: HTTP/SSE bodies are buffered and no upstream POST is
+retried. Credentials and request bodies must never enter diagnostic logs.
+"""
+import asyncio
+import base64
+
+import httpx
+
+from .relay_server import MAX_BODY, PATHS, REQUEST_HEADERS, RESPONSE_HEADERS
+
+
+async def pump(proxy_url, proxy_headers, relay_token, gateway_url, stop, *, timeout=110):
+ headers = {**proxy_headers, 'X-Relay-Token': relay_token}
+ async with httpx.AsyncClient(timeout=timeout, trust_env=False) as upstream, \
+ httpx.AsyncClient(timeout=30, trust_env=False, headers=headers) as platform:
+ while not stop.is_set():
+ pending = await platform.get(proxy_url.rstrip('/') + '/relay/next')
+ if pending.status_code == 204:
+ await asyncio.sleep(0.25)
+ continue
+ pending.raise_for_status()
+ item = pending.json()
+ if (item['method'] != 'POST' or item['path'].split('?', 1)[0] not in PATHS
+ or not isinstance(item['id'], str)
+ or len(item['id']) != 32 or any(c not in '0123456789abcdef' for c in item['id'])):
+ raise ValueError('invalid relay request')
+ body = base64.b64decode(item['body'], validate=True)
+ if len(body) > MAX_BODY:
+ raise ValueError('relay body too large')
+ request_headers = {k: v for k, v in item['headers'].items() if k.lower() in REQUEST_HEADERS}
+ try:
+ async with upstream.stream('POST', gateway_url.rstrip('/') + item['path'],
+ content=body, headers=request_headers) as response:
+ content = bytearray()
+ async for chunk in response.aiter_bytes():
+ content.extend(chunk)
+ if len(content) > MAX_BODY // 2:
+ raise ValueError('relay response too large')
+ result = {'status': response.status_code,
+ 'headers': {k: v for k, v in response.headers.items()
+ if k.lower() in RESPONSE_HEADERS},
+ 'body': base64.b64encode(content).decode()}
+ except (httpx.HTTPError, ValueError):
+ result = {'status': 502, 'headers': {'content-type': 'application/json'},
+ 'body': base64.b64encode(b'{"error":"upstream_failed_no_retry"}').decode()}
+ delivered = await platform.post(proxy_url.rstrip('/') + '/relay/result/' + item['id'], json=result)
+ delivered.raise_for_status()
diff --git a/practices/h_cluster/src/harborrl_h_cluster/transport.py b/practices/h_cluster/src/harborrl_h_cluster/transport.py
new file mode 100644
index 000000000..b72151681
--- /dev/null
+++ b/practices/h_cluster/src/harborrl_h_cluster/transport.py
@@ -0,0 +1,28 @@
+"""Bounded retries for H-platform endpoint discovery, never allocation POSTs."""
+import asyncio
+import re
+import time
+
+from harborrl.rollout.harbor_job.sandbox_transport import (
+ SandboxStartupTransport as AllocationTransport,
+)
+
+
+class SandboxStartupTransport(AllocationTransport):
+ def __init__(self, base_url, on_created, *, timeout, transport=None, retry_delay=2):
+ super().__init__(base_url, on_created, transport=transport)
+ self.deadline = time.monotonic() + timeout
+ self.retry_delay = retry_delay
+
+ async def handle_async_request(self, request):
+ url = str(request.url).split('?', 1)[0]
+ endpoint_get = request.method == 'GET' and re.fullmatch(
+ re.escape(self.base_url) + r'/sandboxes/[A-Za-z0-9_-]+/endpoints/\d+', url)
+ while True:
+ response = await super().handle_async_request(request)
+ if not endpoint_get or response.status_code not in (502, 503, 504):
+ return response
+ if self.deadline - time.monotonic() <= self.retry_delay:
+ return response
+ await response.aclose()
+ await asyncio.sleep(self.retry_delay)
diff --git a/practices/h_cluster/tests/test_environment.py b/practices/h_cluster/tests/test_environment.py
new file mode 100644
index 000000000..95ea0dda3
--- /dev/null
+++ b/practices/h_cluster/tests/test_environment.py
@@ -0,0 +1,69 @@
+import asyncio
+
+import httpx
+import pytest
+from harbor.environments.opensandbox import OpenSandboxEnvironment
+from harborrl_h_cluster.environment import BrainboxEnvironment, validate_environment
+
+
+def settings():
+ return {'domain': 'https://sandbox.example', 'request_timeout_s': 1, 'use_server_proxy': True,
+ 'options': {'project': 'test', 'gateway_transport': 'platform_pull', 'relay_port': 49185,
+ 'relay_timeout_s': 180},
+ 'bindings': [{'image': 'image', 'max_live_instances': 2,
+ 'options': {'environment_id': 'env-test', 'required_mount_paths': ['/data']}}]}
+
+
+@pytest.mark.parametrize('memory,normalized', [('1024Mi', '1Gi'), ('2048Mi', '2Gi'), ('512Mi', '512Mi')])
+def test_resource_units(monkeypatch, memory, normalized):
+ monkeypatch.setattr(OpenSandboxEnvironment, '_build_resource', lambda _: {'cpu': '2', 'memory': memory})
+ assert object.__new__(BrainboxEnvironment)._build_resource() == {'cpu': '2', 'memory': normalized}
+
+
+@pytest.mark.parametrize('change', ['port', 'project', 'binding', 'duplicate'])
+def test_options_reject_invalid_platform_configuration(change):
+ cfg = settings()
+ if change == 'port':
+ cfg['options']['relay_port'] = 44772
+ elif change == 'project':
+ cfg['options']['project'] = ''
+ elif change == 'binding':
+ cfg['bindings'][0]['options']['environment_id'] = '../other'
+ else:
+ cfg['bindings'].append(cfg['bindings'][0])
+ with pytest.raises(ValueError):
+ BrainboxEnvironment.validate_config(cfg)
+
+
+def test_platform_routing_and_extensions():
+ cfg = settings()
+ BrainboxEnvironment.validate_config(cfg)
+ assert BrainboxEnvironment.agent_gateway(cfg, 'http://gateway') == 'http://127.0.0.1:49185'
+ env = object.__new__(BrainboxEnvironment)
+ env.cfg, env.binding = cfg, cfg['bindings'][0]
+ assert env.extensions() == {'environmentId': 'env-test'}
+ assert env.api_params() == {'project': 'test'}
+ cfg['options']['gateway_transport'] = 'direct'
+ assert BrainboxEnvironment.agent_gateway(cfg, 'http://gateway') == 'http://gateway'
+
+
+@pytest.mark.parametrize('ports,capacity,mounts,accepted', [
+ ([44772, 49185], 2, ['/data'], True), ([44772], 2, ['/data'], False),
+ ([44772, 49185], 1, ['/data'], False), ([44772, 49185], 2, [], False),
+])
+def test_environment_preflight_checks_platform_contract(monkeypatch, ports, capacity, mounts, accepted):
+ cfg = settings()
+
+ def handler(request):
+ assert request.method == 'GET'
+ assert request.url.params['project'] == 'test'
+ return httpx.Response(200, json={'image': {'uri': 'image'}, 'instanceCapacity': capacity,
+ 'ports': [{'containerPort': p} for p in ports], 'volumes': [{'mountPath': p} for p in mounts]})
+
+ client = httpx.AsyncClient
+ monkeypatch.setattr(httpx, 'AsyncClient', lambda **kw: client(**kw, transport=httpx.MockTransport(handler)))
+ if accepted:
+ assert asyncio.run(validate_environment(cfg, cfg['bindings'][0], 'test-only'))['capacity'] == 2
+ else:
+ with pytest.raises(ValueError):
+ asyncio.run(validate_environment(cfg, cfg['bindings'][0], 'test-only'))
diff --git a/practices/h_cluster/tests/test_relay.py b/practices/h_cluster/tests/test_relay.py
new file mode 100644
index 000000000..b8e98007b
--- /dev/null
+++ b/practices/h_cluster/tests/test_relay.py
@@ -0,0 +1,137 @@
+import asyncio
+import base64
+import contextlib
+import json
+import threading
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+
+import httpx
+import pytest
+from harborrl_h_cluster.relay_server import Mailbox, make_server
+from harborrl_h_cluster.reverse_relay import pump
+
+
+@contextlib.contextmanager
+def serving(server):
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
+ thread.start()
+ try:
+ yield f'http://127.0.0.1:{server.server_port}'
+ finally:
+ server.shutdown()
+ server.server_close()
+ thread.join(5)
+
+
+def test_relay_auth_timeout_and_no_redelivery():
+ async def run(url):
+ async with httpx.AsyncClient(trust_env=False) as client:
+ assert (await client.get(url + '/relay/next')).status_code == 403
+ assert (await client.post(url + '/unexpected', content=b'x')).status_code == 404
+ pending = asyncio.create_task(client.post(url + '/v1/messages', content=b'body'))
+ headers = {'X-Relay-Token': 'secret'}
+ for _ in range(100):
+ item = await client.get(url + '/relay/next', headers=headers)
+ if item.status_code == 200:
+ break
+ await asyncio.sleep(.01)
+ assert item.status_code == 200
+ assert (await client.get(url + '/relay/next', headers=headers)).status_code == 204
+ assert (await pending).status_code == 504
+ delivered = await client.post(url + '/relay/result/' + item.json()['id'],
+ headers=headers, json={'status': 200, 'headers': {}, 'body': base64.b64encode(b'late').decode()})
+ assert delivered.status_code == 409
+ with serving(make_server('127.0.0.1', 0, Mailbox('secret', timeout=.5))) as url:
+ asyncio.run(run(url))
+
+
+@pytest.mark.parametrize('status,content_type,body', [
+ (200, 'text/event-stream', b'event: message_start\ndata: {"id":"test"}\n\n'),
+ (429, 'application/json', b'{"error":"rate_limited"}'),
+])
+def test_reverse_roundtrip_preserves_bytes_and_headers(status, content_type, body):
+ calls = []
+ class Upstream(BaseHTTPRequestHandler):
+ def log_message(self, *args):
+ pass
+ def do_POST(self):
+ calls.append((self.path, self.headers.get('x-api-key'),
+ self.rfile.read(int(self.headers['Content-Length']))))
+ self.send_response(status)
+ self.send_header('Content-Type', content_type)
+ self.send_header('Content-Length', str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+ async def run(relay, gateway):
+ stop = asyncio.Event()
+ task = asyncio.create_task(pump(relay, {}, 'secret', gateway, stop))
+ try:
+ async with httpx.AsyncClient(trust_env=False) as client:
+ response = await client.post(relay + '/v1/messages?beta=true',
+ headers={'x-api-key': 'attempt-token'}, content=b'{"stream":true}')
+ assert response.status_code == status
+ assert response.content == body
+ assert response.headers['content-type'] == content_type
+ finally:
+ stop.set()
+ task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await task
+ with serving(make_server('127.0.0.1', 0, Mailbox('secret'))) as relay, \
+ serving(ThreadingHTTPServer(('127.0.0.1', 0), Upstream)) as gateway:
+ asyncio.run(run(relay, gateway))
+ assert calls == [('/v1/messages?beta=true', 'attempt-token', b'{"stream":true}')]
+
+
+def test_real_gateway_trace_and_session_audit_through_relay(tmp_path):
+ from harborrl.gateway.server import Gateway
+ from harborrl.gateway.server import make_server as make_gateway
+ from harborrl.gateway.trace import TraceRegistry
+ from harborrl.rollout.harbor_job.audit import audit_session
+ from harborrl.trajectories.native import Identity, RewardProfile, digest
+
+ class Backend:
+ def count_tokens(self, converted):
+ return 2
+
+ def generate(self, converted, ident, response_id):
+ return {'input_ids': [1, 2], 'output_ids': [3], 'logprobs': [-.2], 'logprob_semantics': 'raw_model',
+ 'policy_version': ident.policy_version, 'tokenizer_digest': 'tok', 'template_digest': 'tpl',
+ 'engine_id': 'synthetic', 'serving_input': {'input_ids': [1, 2]}, 'sampling': converted['sampling'],
+ 'finish_reason': 'end_turn', 'evidence_kind': 'synthetic', 'content': [{'type': 'text', 'text': 'done'}]}
+
+ sampling = {'temperature': 1.0, 'top_p': 1.0, 'top_k': -1, 'max_new_tokens': 64, 'stop': []}
+ identity = Identity(run_id='run', batch_id='batch', group_id='group', slot_id='0', attempt_id='attempt',
+ trajectory_id='trajectory', task_digest='task', policy_version='1', harness_digest='claude',
+ reward_digest=RewardProfile().digest, sampling_digest=digest(sampling))
+ registry = TraceRegistry(tmp_path / 'traces')
+ credential = registry.register(identity, 'trial')
+ gateway = Gateway(registry, Backend(), model='policy')
+
+ async def run(relay, upstream):
+ stop = asyncio.Event()
+ task = asyncio.create_task(pump(relay, {}, 'secret', upstream, stop))
+ try:
+ async with httpx.AsyncClient(trust_env=False) as client:
+ response = await client.post(relay + '/v1/messages',
+ headers={'x-api-key': credential}, json={'model': 'policy', 'max_tokens': 64,
+ 'messages': [{'role': 'user', 'content': 'hello'}]})
+ assert response.status_code == 200
+ message = response.json()
+ session = tmp_path / 'session.jsonl'
+ session.write_text(json.dumps({'sessionId': 'relay-session', 'type': 'assistant',
+ 'message': message}) + '\n')
+ audit_session(session, registry, identity.attempt_id)
+ registry.close(identity.attempt_id)
+ turns, seal = registry.seal(identity.attempt_id)
+ assert seal['errors'] == []
+ assert len(turns) == 1
+ assert turns[0]['delivery'] == 'consumed_confirmed'
+ finally:
+ stop.set()
+ task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await task
+ with serving(make_server('127.0.0.1', 0, Mailbox('secret'))) as relay, \
+ serving(make_gateway(gateway)) as upstream:
+ asyncio.run(run(relay, upstream))
diff --git a/practices/h_cluster/tests/test_transport.py b/practices/h_cluster/tests/test_transport.py
new file mode 100644
index 000000000..9e56c93df
--- /dev/null
+++ b/practices/h_cluster/tests/test_transport.py
@@ -0,0 +1,66 @@
+import asyncio
+
+import httpx
+from harborrl_h_cluster.transport import SandboxStartupTransport
+
+
+def test_transient_endpoint_retries_do_not_recreate_sandbox():
+ async def run():
+ calls, ids = [], []
+
+ async def serve(request):
+ calls.append((request.method, request.url.path))
+ if request.method == 'POST':
+ return httpx.Response(201, json={'id': 'sbx-one'})
+ if len(calls) < 4:
+ return httpx.Response(503)
+ return httpx.Response(200, json={'endpoint': 'ready'})
+
+ transport = SandboxStartupTransport('https://platform.test/v1', ids.append, timeout=10,
+ transport=httpx.MockTransport(serve), retry_delay=0)
+ async with httpx.AsyncClient(transport=transport) as client:
+ await client.post('https://platform.test/v1/sandboxes', json={})
+ response = await client.get('https://platform.test/v1/sandboxes/sbx-one/endpoints/44772')
+ assert response.status_code == 200
+ assert ids == ['sbx-one']
+ assert sum(method == 'POST' for method, _ in calls) == 1
+ assert len(calls) == 4
+
+ asyncio.run(run())
+
+
+def test_mutations_proxy_requests_and_auth_failures_are_not_retried():
+ async def run():
+ calls = []
+
+ async def serve(request):
+ calls.append(request)
+ return httpx.Response(401 if '/endpoints/' in request.url.path else 503)
+
+ transport = SandboxStartupTransport('https://platform.test/v1', lambda _: None, timeout=10,
+ transport=httpx.MockTransport(serve), retry_delay=0)
+ async with httpx.AsyncClient(transport=transport) as client:
+ await client.post('https://platform.test/v1/sandboxes')
+ await client.get('https://platform.test/v1/sandboxes/sbx-one/proxy/44772')
+ await client.get('https://platform.test/v1/sandboxes/sbx-one/endpoints/44772')
+ assert len(calls) == 3
+
+ asyncio.run(run())
+
+
+def test_endpoint_retry_deadline_returns_final_failure():
+ async def run():
+ calls = []
+
+ async def serve(request):
+ calls.append(request)
+ return httpx.Response(503)
+
+ transport = SandboxStartupTransport('https://platform.test/v1', lambda _: None, timeout=0,
+ transport=httpx.MockTransport(serve), retry_delay=0)
+ async with httpx.AsyncClient(transport=transport) as client:
+ response = await client.get('https://platform.test/v1/sandboxes/sbx-one/endpoints/44772')
+ assert response.status_code == 503
+ assert len(calls) == 1
+
+ asyncio.run(run())
diff --git a/pyproject.toml b/pyproject.toml
index ed654f6bb..1cbdca1ad 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,10 +16,16 @@ dependencies = [
harborrl = "harborrl.cli:main"
[project.optional-dependencies]
+sandbox = [
+ "harbor==0.23.0; python_version >= '3.12'",
+ "opensandbox==0.1.9; python_version >= '3.12'",
+ "httpx>=0.27,<1",
+]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"ruff>=0.8",
+ "httpx>=0.27,<1",
]
[tool.uv]
@@ -31,6 +37,8 @@ exclude = ["runs*", ".venv*", "workspace*", "local*", "docs*", "tests*"]
[tool.setuptools.package-data]
"harborrl.platform" = ["*.sh"]
+"harborrl.options" = ["resources/*.yaml"]
+"harborrl.webui" = ["static/*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
diff --git a/scripts/audit_public_tree.sh b/scripts/audit_public_tree.sh
index 7f80ce653..b9c044203 100755
--- a/scripts/audit_public_tree.sh
+++ b/scripts/audit_public_tree.sh
@@ -4,11 +4,40 @@ set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
-python3 - <<'PY'
+python3 - "$@" <<'PY'
+import argparse
from pathlib import Path
import re
import subprocess
+parser = argparse.ArgumentParser(description="Audit public files and optional new-commit document history.")
+parser.add_argument("--history-base", help="Check every commit after this ancestor, including deleted documents.")
+args = parser.parse_args()
+
+public_documents = {
+ "CONTRIBUTING.md", "NOTICE.md", "README.md", "README_zh.md", "SECURITY.md",
+ "configs/harbor_hub/public_compatibility_summary.md",
+ "configs/harbor_hub/public_compatibility_summary_zh.md",
+ "docs/agents/environment-check.md", "docs/configuration-wizard.md",
+ "docs/deployment.md", "docs/sandbox-rollout.md",
+ "docs/train-inference-consistency.md", "docs/web-console.md",
+ "practices/h_cluster/README.md", "harborrl/webui/static/lucide-LICENSE.txt",
+}
+
+
+def audit_documents(names, location):
+ for name in names:
+ path = Path(name)
+ if path.suffix.lower() not in {".md", ".rst", ".adoc", ".txt"}:
+ continue
+ task_instruction = (
+ path.parts[:3] == ("examples", "native", "tasks")
+ and path.name == "instruction.md"
+ )
+ if name not in public_documents and not task_instruction:
+ raise SystemExit(f"document is not approved for publication ({location}): {name}")
+
+
root = Path(".")
files = subprocess.check_output(
["git", "ls-files", "-z"], text=True
@@ -16,6 +45,23 @@ files = subprocess.check_output(
files = [Path(name) for name in files if name]
if not files:
raise SystemExit("tracked tree is empty")
+audit_documents([path.as_posix() for path in files], "index")
+
+if args.history_base:
+ if subprocess.run(
+ ["git", "merge-base", "--is-ancestor", args.history_base, "HEAD"],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
+ ).returncode:
+ raise SystemExit("history base must resolve to an ancestor of HEAD")
+ commits = subprocess.check_output(
+ ["git", "rev-list", f"{args.history_base}..HEAD"], text=True
+ ).splitlines()
+ for commit in commits:
+ names = subprocess.check_output(
+ ["git", "ls-tree", "-r", "--name-only", "-z", commit], text=True
+ ).split("\0")
+ audit_documents([name for name in names if name], commit[:12])
+ print(f"public document history audit passed: {len(commits)} new commits")
forbidden_directories = {"backends", "benchmarks", "deploy", "runs", "tools"}
for path in files:
@@ -36,10 +82,11 @@ internal_patterns = {
"personal path": re.compile(rb"/(mnt/shared-storage-user|home/[A-Za-z0-9_.-]+|root)/"),
"internal DNS": re.compile(rb"(?i)[a-z0-9.-]+\.(local|internal|svc)\b"),
"private registry": re.compile(rb"(?i)(registry\.[a-z0-9.-]+/(?:[a-z0-9_.-]+/){2,})"),
+ "cluster DNS": re.compile(rb"(?i)[a-z0-9.-]+\.(?:pjlab|p\x6a-lab)\.org\.cn\b"),
}
text_suffixes = {
".cfg", ".env", ".github", ".in", ".json", ".md", ".py", ".sh", ".toml",
- ".txt", ".yaml", ".yml",
+ ".txt", ".yaml", ".yml", ".lock",
}
for path in files:
if path.suffix.lower() not in text_suffixes and path.name != "Dockerfile":
diff --git a/scripts/collect_service_evidence.py b/scripts/collect_service_evidence.py
new file mode 100755
index 000000000..fa55d4add
--- /dev/null
+++ b/scripts/collect_service_evidence.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""Collect service-mode run evidence for the controller-placement validation.
+
+Reads one run directory and reports: exit code, policy versions, checkpoints,
+rollout groups/slots (from the RolloutService artifacts), RL_READY counts,
+rewards, trace seals, integrity errors, and cleanup records. Machine-readable
+JSON goes to stdout; exit 0 only when the mandatory gates hold.
+"""
+from __future__ import annotations
+
+import json
+import sys
+from pathlib import Path
+
+
+def read_json(path):
+ return json.loads(Path(path).read_text())
+
+
+def main(run_dir):
+ run_dir = Path(run_dir)
+ report = {"run_dir": str(run_dir), "gates": {}}
+
+ exit_code = int((run_dir / "exit-code").read_text().strip())
+ report["exit_code"] = exit_code
+ report["gates"]["exit_code"] = exit_code == 0
+
+ # Policy versions: the manager's activation publishes policy-pool.json
+ # (snapshot) and each completed group records its admitted version.
+ versions = []
+ snapshot = run_dir / "policy-pool.json"
+ final_version = None
+ if snapshot.is_file():
+ final_version = read_json(snapshot).get("weight_version")
+ for result in sorted(run_dir.glob("rollout-service/groups/*/group-*/result.json")):
+ versions.append(read_json(result)["policy_version"])
+ report["policy_versions"] = {"groups": versions, "final_pool": final_version}
+ report["gates"]["policy_advanced"] = len(set(versions)) >= 2 and (
+ final_version is not None and str(final_version) == str(versions[-1]))
+
+ checkpoints = sorted(p.name for p in run_dir.glob("checkpoints/*/iter_*")) \
+ if run_dir.joinpath("checkpoints").is_dir() else []
+ report["checkpoints"] = checkpoints
+ report["gates"]["checkpoints_written"] = len(checkpoints) >= 2
+
+ # Service artifacts: groups executed by the manager.
+ groups_root = run_dir / "rollout-service" / "groups"
+ groups = {}
+ if groups_root.is_dir():
+ for run_id_dir in groups_root.iterdir():
+ for group_dir in run_id_dir.iterdir():
+ summary = {"group_id": group_dir.name, "states": [],
+ "has_result": (group_dir / "result.json").is_file()}
+ for state in ("running", "result", "cancelled", "failed"):
+ if (group_dir / f"{state}.json").is_file():
+ summary["states"].append(state)
+ groups[group_dir.name] = summary
+ report["service_groups"] = groups
+ report["gates"]["service_groups_complete"] = bool(groups) and all(
+ g["states"] and "result" in g["states"] and g["has_result"]
+ for g in groups.values())
+
+ # Native rollouts + traces.
+ attempts = list((run_dir / "native-rollouts").rglob("trajectory.v2.json")) \
+ if (run_dir / "native-rollouts").is_dir() else []
+ readiness, rewards, integrity, seals = {}, [], 0, {"sealed": 0, "unsealed": 0}
+ turns_total = 0
+ for path in attempts:
+ ir = read_json(path)
+ readiness[ir["readiness"]] = readiness.get(ir["readiness"], 0) + 1
+ integrity += len(ir.get("integrity_errors", []))
+ rewards.append(ir["evaluation"]["training_reward"])
+ turns_total += len(ir["turns"])
+ if ir["trace_seal"]["status"] == "sealed" and not ir["trace_seal"]["errors"]:
+ seals["sealed"] += 1
+ else:
+ seals["unsealed"] += 1
+ report["trajectories"] = {
+ "count": len(attempts), "readiness": readiness, "rewards": rewards,
+ "turns_total": turns_total, "integrity_errors": integrity, "seals": seals}
+ report["gates"]["all_rl_ready"] = len(attempts) >= 6 and readiness.get("RL_READY") == len(attempts)
+ report["gates"]["zero_integrity_errors"] = integrity == 0
+ report["gates"]["zero_unsealed"] = seals["unsealed"] == 0
+
+ cleanup = [p.name for p in run_dir.rglob("*cleanup-required.json")]
+ unsealed = [p.name for p in run_dir.rglob("*trace-unsealed.json")]
+ failed = [p.name for p in run_dir.rglob("*failure.json")]
+ report["cleanup_records"] = {"cleanup_required": cleanup, "trace_unsealed": unsealed,
+ "failures": failed}
+ report["gates"]["clean_run"] = not (cleanup or unsealed or failed)
+
+ passed = int(exit_code) == 0 and all(report["gates"].values())
+ report["summary"] = ("PASS" if passed else "FAIL") + \
+ f" gates={report['gates']}"
+ print(json.dumps(report, indent=2, ensure_ascii=False))
+ return 0 if passed else 1
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ raise SystemExit("usage: collect_service_evidence.py RUN_DIR")
+ raise SystemExit(main(sys.argv[1]))
diff --git a/scripts/readiness_dial.py b/scripts/readiness_dial.py
new file mode 100755
index 000000000..7268c4563
--- /dev/null
+++ b/scripts/readiness_dial.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+"""Live cross-machine readiness dials for the controller placement validation.
+
+Starts a temporary control plane + a /readyz listener on the manager, then
+dials them from the GPU target and the rollout worker over SSH — the same
+hops the real deployment requires (gpu_to_controller, worker_to_gateway).
+"""
+from __future__ import annotations
+
+import ipaddress
+import json
+import subprocess
+import sys
+import threading
+import time
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+
+from harborrl.rollout.service import RolloutService, make_control_server
+
+
+def ssh(spec, command, timeout=20):
+ result = subprocess.run(
+ ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10",
+ "-o", "StrictHostKeyChecking=accept-new", spec, command],
+ capture_output=True, text=True, timeout=timeout, check=False)
+ return result.returncode == 0, (result.stdout + result.stderr).strip()[-200:]
+
+
+def main(gpu_spec, worker_spec, manager_address):
+ manager_address = str(ipaddress.IPv4Address(manager_address))
+ # 1) a real control plane on the manager's control port
+ service = RolloutService(run_root="/tmp/harborrl-readiness")
+ control = make_control_server(service, host="0.0.0.0", port=49200)
+ control_thread = threading.Thread(target=control.serve_forever, daemon=True)
+ control_thread.start()
+ # 2) a plain /readyz listener standing in for the gateway port
+ class Gateway(ThreadingHTTPServer):
+ daemon_threads = True
+
+ class Handler(BaseHTTPRequestHandler):
+ def log_message(self, *args):
+ pass
+
+ def do_GET(self):
+ body = b'{"ok": true}'
+ self.send_response(200)
+ self.send_header("Content-Type", "application/json")
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ gateway = Gateway(("0.0.0.0", 49184), Handler)
+ gateway_thread = threading.Thread(target=gateway.serve_forever, daemon=True)
+ gateway_thread.start()
+ time.sleep(1)
+
+ checks = []
+ checks.append(("manager.control_plane_listening",
+ *ssh("localhost", "true"))) # manager-local sanity
+ # Direct local health check of the control plane.
+ local = subprocess.run(
+ ["python3", "-c",
+ "import urllib.request;urllib.request.urlopen('http://127.0.0.1:49200/healthz',timeout=5)"],
+ capture_output=True, text=True, timeout=10, check=False)
+ checks.append(("manager.control_healthz_local", local.returncode == 0,
+ "healthz answered on the manager"))
+ checks.append(("gpu_to_controller",
+ *ssh(gpu_spec,
+ "python3 -c \"import urllib.request;"
+ f"urllib.request.urlopen('http://{manager_address}:49200/healthz',"
+ "timeout=10)\"")))
+ checks.append(("worker_to_gateway",
+ *ssh(worker_spec,
+ "python3 -c \"import urllib.request;"
+ f"urllib.request.urlopen('http://{manager_address}:49184/readyz',"
+ "timeout=10)\"")))
+
+ control.shutdown()
+ gateway.shutdown()
+ report = [{"check": name, "ok": ok, "detail": detail}
+ for name, ok, detail in checks]
+ passed = all(item["ok"] for item in report)
+ print(json.dumps({"readiness": report,
+ "summary": "PASS" if passed else "FAIL"}, indent=2))
+ return 0 if passed else 1
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 4:
+ raise SystemExit("usage: readiness_dial.py GPU_SSH_SPEC WORKER_SSH_SPEC MANAGER_IPV4")
+ raise SystemExit(main(sys.argv[1], sys.argv[2], sys.argv[3]))
diff --git a/scripts/rollout_service_smoke.py b/scripts/rollout_service_smoke.py
new file mode 100755
index 000000000..c0de2e1a9
--- /dev/null
+++ b/scripts/rollout_service_smoke.py
@@ -0,0 +1,263 @@
+#!/usr/bin/env python3
+"""CPU smoke for the RolloutService control plane (no GPU, no Harbor, no SSH).
+
+Starts a real control server on loopback with a synthetic executor, then
+drives the full lifecycle through the production HTTP client: register,
+policy gate, activate, idempotent submit, conflict rejection, digest
+validation, cancel, drain. Exits 0 only when every contract holds.
+"""
+from __future__ import annotations
+
+import asyncio
+import json
+import tempfile
+import threading
+from pathlib import Path
+from types import SimpleNamespace
+
+from harborrl.backends.slime_v032.rollout_service_client import (
+ RolloutServiceClient,
+ ServiceRolloutError,
+ load_group_result,
+)
+from harborrl.rollout.harbor_job.collector import collect
+from harborrl.rollout.service import (
+ RolloutService,
+ ServiceError,
+ make_control_server,
+ manifest_path_from,
+)
+from harborrl.trajectories.native import (
+ Identity,
+ RewardProfile,
+ assemble,
+ digest,
+ publish,
+)
+
+SAMPLING = {"temperature": 1.0, "top_p": 1.0, "top_k": -1,
+ "max_new_tokens": 64, "stop": []}
+TASK = {"id": "smoke-task", "revision": "1", "path": "/task",
+ "task_digest": "d" * 64,
+ "reward_profile": {"key": "reward", "scale": 1.0, "offset": 0.0,
+ "raw_range": [0.0, 1.0]}}
+RUN_ID = "smoke-run-1"
+
+
+def step(name, ok, detail=""):
+ status = "PASS" if ok else "FAIL"
+ print(f"[{status}] {name}" + (f" — {detail}" if detail else ""))
+ return ok
+
+
+def identity(slot, *, group_id, policy_version):
+ return Identity(run_id=RUN_ID, batch_id="rollout-0", group_id=group_id,
+ slot_id=slot, attempt_id=f"attempt-{policy_version}-{slot}",
+ trajectory_id=f"trajectory-{policy_version}-{slot}",
+ task_digest=TASK["task_digest"],
+ policy_version=policy_version, harness_digest="claude",
+ reward_digest=RewardProfile().digest,
+ sampling_digest=digest(SAMPLING))
+
+
+def synthetic_ir(root, slot, reward, *, group_id, policy_version):
+ ident = identity(slot, group_id=group_id, policy_version=policy_version)
+ attempt_root = root / "receipts" / ident.attempt_id
+ attempt_root.mkdir(parents=True, exist_ok=True)
+ result = {"id": "trial-" + ident.attempt_id,
+ "finished_at": "2026-10-21T00:00:00Z",
+ "verifier_environment_mode": "shared",
+ "verifier_result": {"rewards": {"reward": reward}}}
+ (attempt_root / "result.json").write_text(json.dumps(result))
+ (attempt_root / "reward.json").write_text(json.dumps({"reward": reward}))
+ evaluation = collect(ident, result["id"], attempt_root / "result.json",
+ attempt_root, RewardProfile(), evidence_kind="synthetic")
+ trial = evaluation["harbor_trial_id"]
+ turn = {"identity": ident.to_dict(), "harbor_trial_id": trial,
+ "request_id": f"r-{slot}", "response_id": f"m-{slot}",
+ "engine_id": "engine", "input_ids": [1, 2], "output_ids": [3, 4],
+ "logprobs": [-0.2, -0.3], "logprob_semantics": "raw_model",
+ "policy_version": policy_version, "tokenizer_digest": "tok",
+ "template_digest": "template", "delivery": "consumed_confirmed",
+ "consumption_ref": "session#digest", "role": "policy",
+ "sampling": SAMPLING, "client_request": {"model": "policy"},
+ "serving_input": {"input_ids": [1, 2]},
+ "response": {"content": [{"type": "text", "text": "done"}]},
+ "finish_reason": "end_turn", "evidence_kind": "synthetic"}
+ for key in ("client_request", "serving_input", "response"):
+ turn[key + "_digest"] = digest(turn[key])
+ seal = {"identity": ident.to_dict(), "harbor_trial_id": trial,
+ "pending_requests": 0, "status": "sealed",
+ "request_ids": [turn["request_id"]], "turns_digest": digest([turn]),
+ "errors": []}
+ return assemble(ident, trial, [turn], seal, evaluation)
+
+
+class Registry:
+ def __init__(self):
+ self.version, self.accepting = None, False
+
+ def drain(self, timeout):
+ self.accepting = False
+
+ def publish_version(self, states, expected):
+ self.version, self.accepting = str(expected), True
+
+
+class Runtime:
+ def __init__(self, root):
+ self.registry, self.rollout_root = Registry(), root
+
+ def manifest_path(self, ident):
+ return manifest_path_from(self.rollout_root, ident)
+
+ def close(self):
+ pass
+
+
+def group_request(group_index, *, policy_version="1", slots=2):
+ return {"group_id": f"group-{group_index}", "rollout_id": 0,
+ "group_index": group_index, "policy_version": policy_version,
+ "task": TASK, "sampling": dict(SAMPLING),
+ "slots": [{"slot_id": str(slot)} for slot in range(slots)]}
+
+
+def main():
+ root = Path(tempfile.mkdtemp(prefix="harborrl-service-smoke-"))
+ executions = []
+
+ async def executor(config, runtime_, request):
+ executions.append(request["group_id"])
+ if request["group_index"] >= 1:
+ while True: # later groups block until they are cancelled
+ await asyncio.sleep(0.05)
+ irs = []
+ for descriptor, reward in zip(request["slots"], (1.0, 0.0)):
+ ir = synthetic_ir(root, descriptor["slot_id"], reward,
+ group_id=request["group_id"],
+ policy_version=request["policy_version"])
+ publish(runtime_.manifest_path(ir["identity"]), ir)
+ irs.append(ir)
+ return irs
+
+ runtime = Runtime(root)
+ service = RolloutService(run_root=root, executor=executor, allow_synthetic=True,
+ group_wait_timeout=10)
+ # The smoke owns the service process, so it registers with its own token
+ # instead of consuming the one-time generated token.
+ service.register_run(RUN_ID, {"run_id": RUN_ID, "group_size": 2,
+ "purpose": "cpu service smoke"},
+ token="smoke-token")
+ service.attach_runtime(RUN_ID, SimpleNamespace(drain_timeout=1), runtime)
+ control = make_control_server(service, host="127.0.0.1", port=0)
+ thread = threading.Thread(target=control.serve_forever, daemon=True)
+ thread.start()
+ url = f"http://127.0.0.1:{control.server_address[1]}"
+ client = RolloutServiceClient(url, RUN_ID, "smoke-token", timeout=10,
+ poll_interval=0.05)
+
+ results = []
+ try:
+ results.append(step("healthz answers", client.health()["ok"]))
+ # Register is idempotent by manifest digest.
+ replay = service.register_run(RUN_ID, {"run_id": RUN_ID,
+ "group_size": 2,
+ "purpose": "cpu service smoke"})
+ results.append(step("register replay is idempotent", replay["existed"]))
+ try:
+ service.register_run(RUN_ID, {"run_id": RUN_ID, "other": 1})
+ results.append(step("manifest conflict rejected", False))
+ except ServiceError as exc:
+ results.append(step("manifest conflict rejected", exc.status == 409))
+
+ # Auth: a wrong token never reaches group endpoints.
+ bad = RolloutServiceClient(url, RUN_ID, "wrong", timeout=10)
+ try:
+ bad.group_status("group-0")
+ results.append(step("bad token rejected", False))
+ except ServiceRolloutError as exc:
+ results.append(step("bad token rejected", exc.status == 401))
+
+ # Policy gate: nothing runs before activation.
+ try:
+ client.submit_group(group_request(0))
+ results.append(step("submit before activate rejected", False))
+ except ServiceRolloutError as exc:
+ results.append(step("submit before activate rejected",
+ exc.code == "stale_policy"))
+
+ states = [{"endpoint": f"http://engine-{i}", "healthy": True,
+ "weight_version": "1"} for i in range(2)]
+ activation = client.activate_policy(states, "1")
+ results.append(step("policy activation opens admission",
+ activation["policy_version"] == "1"
+ and runtime.registry.accepting))
+ pool = json.loads((root / "policy-pool.json").read_text())
+ results.append(step("policy pool published to shared run dir",
+ pool["weight_version"] == "1"))
+
+ request = group_request(0)
+ first = client.submit_group(request)
+ results.append(step("group completes with two slots",
+ first["status"] == "complete"
+ and len(first["result"]["slots"]) == 2))
+ assert client.group_status("group-0")["status"] == "complete"
+ again = client.submit_group(json.loads(json.dumps(request)))
+ results.append(step("resubmit is idempotent",
+ again["result"]["request_digest"]
+ == first["result"]["request_digest"]
+ and executions.count("group-0") == 1,
+ f"executions={executions}"))
+ conflicting = group_request(0)
+ conflicting["sampling"]["temperature"] = 0.5
+ try:
+ client.submit_group(conflicting)
+ results.append(step("same id different content rejected", False))
+ except ServiceRolloutError as exc:
+ results.append(step("same id different content rejected",
+ exc.code == "conflict"))
+
+ trajectories = load_group_result(
+ RUN_ID, request, first["result"],
+ allow_synthetic=any(slot["readiness"] != "RL_READY"
+ for slot in first["result"]["slots"]))
+ results.append(step("client validates manifests and digests",
+ [ir["identity"]["slot_id"] for ir in trajectories]
+ == ["0", "1"]))
+ export_path = Path(first["result"]["group_export"]["path"])
+ export = json.loads(export_path.read_text())
+ results.append(step("group export lands on shared storage",
+ export["trajectory_count"] == 2
+ and export["mean"] == 0.5, str(export_path.relative_to(root))))
+
+ # Cancel a group that is still running, then stop admitting.
+ slow = group_request(1)
+ service.group_wait_timeout = 0.2
+ pending = service.submit_group(RUN_ID, slow)
+ results.append(step("timeout answer says running and points at status",
+ pending["status"] in ("queued", "running")
+ and "hint" in pending))
+ cancelled = client.cancel_group("group-1")
+ results.append(step("running group cancels",
+ cancelled["status"] == "cancelled"))
+
+ client.drain()
+ try:
+ client.submit_group(group_request(2))
+ results.append(step("drain closes admission", False))
+ except ServiceRolloutError as exc:
+ results.append(step("drain closes admission",
+ exc.code == "admission_closed"))
+ finally:
+ control.shutdown()
+ control.server_close()
+ thread.join(5)
+ service.close()
+
+ print(f"\nrun root: {root}")
+ print(f"{sum(results)}/{len(results)} checks passed")
+ return 0 if all(results) else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/scripts/sandbox_sdk_contract.py b/scripts/sandbox_sdk_contract.py
new file mode 100644
index 000000000..9957d6ee7
--- /dev/null
+++ b/scripts/sandbox_sdk_contract.py
@@ -0,0 +1,60 @@
+"""Check the actual optional SDK/Harbor imports without allocating resources."""
+import asyncio
+import inspect
+import json
+import tempfile
+from pathlib import Path
+from types import SimpleNamespace
+
+from harbor.models.trial.config import TrialConfig
+from opensandbox import Sandbox
+
+from harborrl.harnesses.offline_claude import OfflineClaudeCode
+from harborrl.rollout.harbor_job.environments.opensandbox import (
+ ManagedOpenSandboxEnvironment,
+)
+from harborrl.trajectories.native import read_json
+
+sdk = ManagedOpenSandboxEnvironment._load_opensandbox(None)
+assert {'extensions', 'skip_health_check', 'ready_timeout'} <= inspect.signature(Sandbox.create).parameters.keys()
+assert 'import_path' in TrialConfig.model_fields['environment'].annotation.model_fields
+assert not inspect.isabstract(OfflineClaudeCode)
+
+
+async def cleanup_contract():
+ with tempfile.TemporaryDirectory() as directory:
+ env = object.__new__(ManagedOpenSandboxEnvironment)
+ env._startup_transport = None
+ env.cfg = {'cleanup_timeout_s': 1}
+ env.evidence = Path(directory)
+ env.binding = {'image': 'image-test'}
+ env.record = {'sandbox_id': 'sandbox-test', 'runtime_digest': 'test', 'events': []}
+ calls = []
+
+ async def kill():
+ calls.append('kill')
+
+ async def close():
+ calls.append('close')
+
+ env._sandbox = SimpleNamespace(kill=kill, close=close)
+ await env.stop()
+ assert calls == ['kill', 'close']
+ assert read_json(env.evidence / 'sandbox-lifecycle.json')['state'] == 'deleted'
+
+ async def failed_kill():
+ raise RuntimeError('secret-must-not-appear')
+
+ env._sandbox = SimpleNamespace(kill=failed_kill, close=close)
+ try:
+ await env.stop()
+ except RuntimeError as exc:
+ assert str(exc) == 'Sandbox deletion failed'
+ else:
+ raise AssertionError('deletion failure was swallowed')
+ assert 'secret-must-not-appear' not in (env.evidence / 'cleanup-required.json').read_text()
+ assert read_json(env.evidence / 'sandbox-lifecycle.json')['state'] == 'delete_failed'
+
+
+asyncio.run(cleanup_contract())
+print(json.dumps({'sdk_symbols': sorted(sdk), 'agent': OfflineClaudeCode.__name__, 'status': 'passed'}))
diff --git a/scripts/sandbox_smoke.py b/scripts/sandbox_smoke.py
new file mode 100644
index 000000000..c297c381d
--- /dev/null
+++ b/scripts/sandbox_smoke.py
@@ -0,0 +1,65 @@
+"""Explicit single-instance live probe. Uses real quota and always attempts deletion."""
+import argparse
+import asyncio
+import json
+import shlex
+from pathlib import Path
+from urllib.parse import urlsplit
+
+from harbor.models.task.config import EnvironmentConfig
+from harbor.models.trial.paths import TrialPaths
+
+from harborrl.rollout.harbor_job.sandbox_config import environment_class, load_sandbox
+from harborrl.rollout.harbor_job.sandbox_probe import check
+from harborrl.trajectories.native import publish
+
+
+async def run(args):
+ cfg = load_sandbox(args.config)
+ check(cfg)
+ root = Path(args.output).resolve()
+ root.mkdir(parents=True, exist_ok=False)
+ task = root / 'environment'
+ task.mkdir()
+ binding = cfg['bindings'][0]
+ adapter = environment_class(cfg)
+ env = adapter(
+ environment_dir=task, environment_name='sandbox-smoke', session_id=root.name,
+ trial_paths=TrialPaths(root / 'trial'), task_env_config=EnvironmentConfig(docker_image=binding['image']),
+ sandbox_config=cfg, binding=binding, evidence_dir=str(root), startup_dir=str(root / 'startup'),
+ gateway_url=args.gateway)
+ results = {}
+ try:
+ await env.start(False)
+ ok = await env.exec('printf sandbox-ok')
+ assert ok.return_code == 0 and ok.stdout == 'sandbox-ok', 'positive command failed'
+ negative = await env.exec('exit 7')
+ assert negative.return_code == 7, 'negative command exit code lost'
+ source = root / 'source.bin'
+ source.write_bytes(b'harborrl\x00sandbox\xff\n')
+ await env.upload_file(source, '/tmp/harborrl-smoke.bin')
+ await env.download_file('/tmp/harborrl-smoke.bin', root / 'download.bin')
+ assert source.read_bytes() == (root / 'download.bin').read_bytes(), 'file roundtrip failed'
+ agent_gateway = adapter.agent_gateway(cfg, args.gateway)
+ gateway = urlsplit(agent_gateway)
+ if gateway.scheme not in ('http', 'https') or not gateway.hostname or gateway.path not in ('', '/'):
+ raise ValueError('gateway must be an HTTP origin')
+ command = f'curl -sS -o /dev/null --connect-timeout 10 --max-time 20 {shlex.quote(agent_gateway)}'
+ connected = await env.exec(command)
+ publish(root / 'gateway-probe.json', {'exit_code': connected.return_code,
+ 'stdout': connected.stdout, 'stderr': connected.stderr})
+ assert connected.return_code == 0, 'Sandbox cannot reach Gateway HTTP endpoint'
+ results.update(command=True, negative_exit=True, files=True, gateway_http=True)
+ finally:
+ await env.stop(True)
+ results['deleted'] = True
+ publish(root / 'smoke-result.json', results)
+ print(json.dumps(results))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('config')
+ parser.add_argument('--output', required=True)
+ parser.add_argument('--gateway', required=True)
+ asyncio.run(run(parser.parse_args()))
diff --git a/scripts/sync_options_manifest.py b/scripts/sync_options_manifest.py
new file mode 100644
index 000000000..ea0370ef3
--- /dev/null
+++ b/scripts/sync_options_manifest.py
@@ -0,0 +1,22 @@
+"""Generate the repository compatibility manifest from packaged metadata."""
+import argparse
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--check", action="store_true")
+ args = parser.parse_args()
+ source = ROOT / "harborrl/options/resources/datasets.yaml"
+ target = ROOT / "configs/harbor_hub/manifests.yaml"
+ expected = source.read_text(encoding="utf-8")
+ if args.check:
+ return int(target.read_text(encoding="utf-8") != expected)
+ target.write_text(expected, encoding="utf-8")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tests/harborrl/test_delivery_barrier.py b/tests/harborrl/test_delivery_barrier.py
new file mode 100644
index 000000000..e6c0f0955
--- /dev/null
+++ b/tests/harborrl/test_delivery_barrier.py
@@ -0,0 +1,41 @@
+"""Delivery acknowledgements must precede native-session consumption audit."""
+import threading
+
+import pytest
+
+from harborrl.gateway.trace import TraceRegistry
+
+
+def test_delivery_barrier_waits_and_wakes(tmp_path):
+ registry = TraceRegistry(tmp_path)
+ registry.attempts["attempt"] = {"pending": {"request"}, "turns": []}
+ entered = threading.Event()
+ finished = threading.Event()
+
+ def wait():
+ entered.set()
+ registry.wait_delivered("attempt", 2)
+ finished.set()
+
+ worker = threading.Thread(target=wait)
+ worker.start()
+ assert entered.wait(1)
+ assert not finished.is_set()
+ with registry.changed:
+ registry.attempts["attempt"]["pending"].clear()
+ registry.attempts["attempt"]["turns"].append({"delivery": "sent"})
+ registry.changed.notify_all()
+ worker.join(3)
+ assert finished.is_set()
+ assert not worker.is_alive()
+
+
+def test_delivery_barrier_rejects_pending_and_unknown(tmp_path):
+ registry = TraceRegistry(tmp_path)
+ registry.attempts["attempt"] = {"pending": {"request"}, "turns": []}
+ with pytest.raises(TimeoutError, match="pending"):
+ registry.wait_delivered("attempt", 0)
+ registry.attempts["attempt"]["pending"].clear()
+ registry.attempts["attempt"]["turns"].append({"delivery": "delivery_unknown"})
+ with pytest.raises(ValueError, match="not confirmed"):
+ registry.wait_delivered("attempt", 0)
diff --git a/tests/harborrl/test_deployment_config.py b/tests/harborrl/test_deployment_config.py
new file mode 100644
index 000000000..8e6eb9f46
--- /dev/null
+++ b/tests/harborrl/test_deployment_config.py
@@ -0,0 +1,176 @@
+"""Deployment schema v1: parsing, strictness, safety and defaults."""
+from __future__ import annotations
+
+import json
+
+import pytest
+
+from harborrl.deployment.schema import (
+ DeploymentError,
+ embedded_local,
+ load_deployment,
+ summary,
+ validate,
+)
+
+
+def deployment_mapping(tmp_path, **overrides):
+ values = {
+ "schema_version": 1,
+ "manager": {
+ "id": "manager-a", "mode": "service", "bind": "0.0.0.0",
+ "control_port": 49200, "control_url": "http://manager-a:49200",
+ "gateway_port": 49184, "gateway_url": "http://manager-a:49184",
+ "run_token_file": str(tmp_path / "secrets" / "run-token"),
+ "source_dir": str(tmp_path / "manager-source"),
+ "controller_python": str(tmp_path / "manager-venv" / "bin" / "python"),
+ },
+ "gpu_target": {
+ "id": "gpu-8x-h200", "transport": "ssh",
+ "ssh": {"host": "gpu-cluster-a"},
+ "source_dir": str(tmp_path / "gpu-source"),
+ "train_python": str(tmp_path / "gpu-venv" / "bin" / "python"),
+ "environment_file": str(tmp_path / "gpu-env.json"),
+ "expected_gpus": 8,
+ },
+ "storage": {
+ "mode": "shared_fs", "run_root": str(tmp_path / "runs"),
+ "manager_writable": True, "gpu_writable": True, "worker_writable": True,
+ },
+ "rollout": {
+ "provider": "ssh_docker",
+ "workers": [
+ {"id": "worker-a", "ssh": {"host": "worker-a"},
+ "source_dir": str(tmp_path / "worker-source"),
+ "runner_python": str(tmp_path / "runner-venv" / "bin" / "python"),
+ "docker": True, "task_roots": [str(tmp_path / "tasks")]},
+ {"id": "worker-b", "ssh": {"host": "worker-b", "port": 2222},
+ "source_dir": str(tmp_path / "worker-source"),
+ "runner_python": str(tmp_path / "runner-venv" / "bin" / "python"),
+ "docker": True, "task_roots": [str(tmp_path / "tasks")]},
+ ],
+ },
+ "connectivity": {
+ "command_timeout_s": 30, "ssh_timeout_s": 15, "http_timeout_s": 10,
+ "gpu_to_controller": True, "worker_to_gateway": True,
+ "manager_to_gpu_inference": True,
+ },
+ }
+ for section, fields in overrides.items():
+ if section in values and isinstance(values[section], dict) and isinstance(fields, dict):
+ values[section].update(fields)
+ else:
+ values[section] = fields
+ return values
+
+
+def test_valid_deployment_normalizes_and_summarizes(tmp_path):
+ config = validate(deployment_mapping(tmp_path))
+ assert config["manager"]["control_url"] == "http://manager-a:49200"
+ assert config["rollout"]["workers"][1]["ssh"] == {"host": "worker-b", "port": 2222}
+ assert config["gpu_target"]["ssh"] == {"host": "gpu-cluster-a"}
+ plan_summary = summary(config, "deployment.yaml")
+ assert plan_summary["mode"] == "service"
+ assert plan_summary["gpu"]["transport"] == "ssh"
+ assert plan_summary["worker_ids"] == ["worker-a", "worker-b"]
+ assert plan_summary["config"] == config
+ assert summary(config, "deployment.yaml")["digest"] == plan_summary["digest"]
+
+
+@pytest.mark.parametrize("mutation, message", [
+ ({"manager": {"mode": "service", "control_url": "http://127.0.0.1:49200"}}, "loopback"),
+ ({"manager": {"control_url": "http://manager-a:49300"}}, "matching its declared port"),
+ ({"manager": {"gateway_port": 49200}}, "must differ"),
+ ({"manager": {"bind": ""}}, "bind"),
+ ({"gpu_target": {"transport": "local", "ssh": {"host": "gpu"}}}, "null for local"),
+ ({"gpu_target": {"transport": "rdma"}}, "transport"),
+ ({"gpu_target": {"expected_gpus": 0}}, "expected_gpus"),
+ ({"rollout": {"workers": []}}, "nonempty list"),
+ ({"connectivity": {"ssh_timeout_s": 0}}, "ssh_timeout_s"),
+ ({"storage": {"run_root": "relative/runs"}}, "absolute path"),
+])
+def test_invalid_deployments_fail_closed(tmp_path, mutation, message):
+ with pytest.raises(DeploymentError, match=message):
+ validate(deployment_mapping(tmp_path, **mutation))
+
+
+def test_duplicate_worker_ids_and_secret_fields_rejected(tmp_path):
+ values = deployment_mapping(tmp_path)
+ values["rollout"]["workers"][1]["id"] = "worker-a"
+ with pytest.raises(DeploymentError, match="duplicate"):
+ validate(values)
+ values = deployment_mapping(tmp_path)
+ values["gpu_target"]["ssh"]["password"] = "hunter2"
+ with pytest.raises(DeploymentError, match="never stored"):
+ validate(values)
+
+
+def test_service_mode_requires_reachable_urls_but_embedded_allows_loopback(tmp_path):
+ values = deployment_mapping(tmp_path, manager={"mode": "embedded"})
+ values["manager"]["control_url"] = "http://127.0.0.1:49200"
+ values["manager"]["gateway_url"] = "http://127.0.0.1:49184"
+ assert validate(values)["manager"]["mode"] == "embedded"
+
+
+def test_embedded_local_default_is_valid():
+ config = embedded_local(run_root="/tmp/harborrl-runs", workers=("worker-a",))
+ assert config["manager"]["mode"] == "embedded"
+ assert config["gpu_target"]["transport"] == "local"
+ assert config["rollout"]["provider"] == "embedded"
+ assert [worker["id"] for worker in config["rollout"]["workers"]] == ["worker-0"]
+ # Training-config SSH destinations are addresses, not ids: the embedded
+ # topology keeps slug ids while preserving the destination for access.
+ config = embedded_local(workers=("root@192.0.2.10", "worker-b"))
+ assert [worker["id"] for worker in config["rollout"]["workers"]] == [
+ "worker-0", "worker-1"]
+ assert config["rollout"]["workers"][0]["ssh"]["host"] == "root@192.0.2.10"
+
+
+def test_load_deployment_explicit_default_and_embedded(tmp_path, monkeypatch):
+ monkeypatch.chdir(tmp_path)
+ config, source = load_deployment()
+ assert source == "embedded-local"
+ assert config["manager"]["mode"] == "embedded"
+
+ (tmp_path / "deployment.yaml").write_text(
+ json.dumps(deployment_mapping(tmp_path)))
+ config, source = load_deployment()
+ assert source == str(tmp_path / "deployment.yaml")
+ assert config["manager"]["mode"] == "service"
+ with pytest.raises(DeploymentError, match="not found"):
+ load_deployment(tmp_path / "missing.yaml")
+
+
+def test_cli_dry_run_embeds_deployment_summary(tmp_path, capsys, monkeypatch):
+ from pathlib import Path
+
+ from test_native_training import native_config
+
+ config_path, task = native_config(tmp_path)
+ from harborrl.data.harbor.native_inspector import inspect_native
+
+ catalog_path = Path(
+ json.loads(config_path.read_text().split("tasks: ", 1)[1].split("\n", 1)[0])["catalog"])
+ catalog = json.loads(catalog_path.read_text())
+ catalog[0]["task_digest"] = inspect_native(task)["task_digest"]
+ catalog_path.write_text(json.dumps(catalog))
+
+ deployment_file = tmp_path / "deployment.yaml"
+ deployment_file.write_text(json.dumps(deployment_mapping(tmp_path)))
+
+ from harborrl.cli import main
+
+ assert main(["train", str(config_path), "--deployment", str(deployment_file),
+ "--dry-run"]) == 0
+ plan = json.loads(capsys.readouterr().out)
+ assert plan["deployment"]["mode"] == "service"
+ assert plan["deployment"]["gpu"]["transport"] == "ssh"
+ assert plan["deployment"]["source"] == str(deployment_file)
+
+ # Without a deployment file the embedded-local topology is attached.
+ monkeypatch.chdir(tmp_path)
+ deployment_file.unlink()
+ assert main(["train", str(config_path), "--dry-run"]) == 0
+ plan = json.loads(capsys.readouterr().out)
+ assert plan["deployment"]["mode"] == "embedded"
+ assert plan["deployment"]["source"] == "embedded-local"
diff --git a/tests/harborrl/test_deployment_preflight.py b/tests/harborrl/test_deployment_preflight.py
new file mode 100644
index 000000000..d7906c9be
--- /dev/null
+++ b/tests/harborrl/test_deployment_preflight.py
@@ -0,0 +1,241 @@
+"""Deployment preflight graph: role-layered checks with faked SSH transport."""
+from __future__ import annotations
+
+import json
+import socket
+import subprocess
+from pathlib import Path
+
+from test_deployment_config import deployment_mapping
+from test_native_training import native_config
+
+from harborrl.deployment import preflight as deployment_preflight
+from harborrl.deployment.schema import embedded_local, validate
+
+
+def test_bindable_rejects_existing_listener():
+ with socket.socket() as listener:
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ listener.bind(('127.0.0.1', 0))
+ listener.listen()
+ ok, _ = deployment_preflight._bindable(*listener.getsockname())
+ assert not ok
+
+
+def test_bindable_allows_http_server_restart_after_connection_close():
+ with socket.socket() as listener:
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ listener.bind(('127.0.0.1', 0))
+ listener.listen()
+ address = listener.getsockname()
+ with socket.create_connection(address, timeout=2) as client:
+ accepted, _ = listener.accept()
+ with accepted:
+ accepted.settimeout(2)
+ accepted.shutdown(socket.SHUT_WR)
+ assert client.recv(1) == b''
+ client.shutdown(socket.SHUT_WR)
+ assert accepted.recv(1) == b''
+ ok, detail = deployment_preflight._bindable(*address)
+ assert ok, detail
+
+
+def completed(returncode=0, stdout="", stderr=""):
+ return subprocess.CompletedProcess([], returncode, stdout, stderr)
+
+
+def fake_remote(responses):
+ """Responses keyed by command prefix; unmatched shell tests succeed."""
+ calls = []
+ probe = json.dumps({"harbor_version": "0.23.0", "python": "3.12.13"})
+
+ def run_ssh(spec, command, *, timeout=30, connect_timeout=15):
+ calls.append((dict(spec), command))
+ for prefix, result in responses.items():
+ if command.startswith(prefix):
+ return completed(**result) if isinstance(result, dict) else result
+ if "runner --probe" in command:
+ return completed(stdout=probe)
+ if command.startswith("test "):
+ return completed(stdout="ok\n") # shell test protocol: && echo ok
+ return completed()
+
+ return run_ssh, calls
+
+
+def resolvable(deployment):
+ """Point service URLs at loopback so preflight resolution checks pass."""
+ manager = deployment["manager"]
+ manager["control_url"] = f"http://127.0.0.1:{manager['control_port']}"
+ manager["gateway_url"] = f"http://127.0.0.1:{manager['gateway_port']}"
+ return deployment
+
+
+def service_deployment(tmp_path):
+ return validate(deployment_mapping(tmp_path))
+
+
+def training_plan(tmp_path):
+ config_path, task = native_config(tmp_path)
+ from harborrl.config import load_config
+ from harborrl.data.harbor.native_inspector import inspect_native
+
+ catalog_path = Path(
+ json.loads(config_path.read_text().split("tasks: ", 1)[1].split("\n", 1)[0])["catalog"])
+ catalog = json.loads(catalog_path.read_text())
+ catalog[0]["task_digest"] = inspect_native(task)["task_digest"]
+ catalog_path.write_text(json.dumps(catalog))
+ return {"config": load_config(config_path)}
+
+
+def by_name(checks, name):
+ return [check for check in checks if check.get("check") == name or check.get("path") == name]
+
+
+def test_gpu_layer_probes_remote_machine_semantics(tmp_path, monkeypatch):
+ plan, deployment = training_plan(tmp_path), resolvable(service_deployment(tmp_path))
+ (tmp_path / "runs").mkdir()
+ (tmp_path / "secrets").mkdir()
+ token = tmp_path / "secrets" / "run-token"
+ token.write_text("token\n")
+ token.chmod(0o600)
+ run_ssh, calls = fake_remote({
+ "nvidia-smi": completed(stdout="0\n1\n2\n3\n4\n5\n6\n7\n"),
+ })
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ assert all(check["ok"] for check in checks), checks
+ gpu_checks = [check for check in checks if check["role"] == "gpu_target"]
+ assert {check["check"] for check in gpu_checks} >= {
+ "gpu_target.ssh", "gpu_target.source_dir", "gpu_target.train_python",
+ "gpu_target.expected_gpus"}
+ assert all(check["machine"] == "gpu-8x-h200" for check in gpu_checks)
+ # Model paths are evaluated on the GPU machine, not from the manager.
+ tested = [command for _, command in calls if "test -d" in command]
+ assert any("/model" in command for command in tested)
+
+
+def test_gpu_layer_reports_shortage_and_unreachable_ssh(tmp_path, monkeypatch):
+ plan, deployment = training_plan(tmp_path), service_deployment(tmp_path)
+ (tmp_path / "runs").mkdir()
+ token = tmp_path / "secrets" / "run-token"
+ token.parent.mkdir()
+ token.write_text("token\n")
+ token.chmod(0o600)
+ run_ssh, _ = fake_remote({"nvidia-smi": completed(stdout="0\n1\n")})
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ shortage = by_name(checks, "gpu_target.expected_gpus")
+ assert shortage and not shortage[0]["ok"]
+ assert "visible GPUs" in shortage[0]["detail"]
+
+ run_ssh, _ = fake_remote({"": completed(returncode=255, stderr="Permission denied")})
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ unreachable = by_name(checks, "gpu_target.ssh")
+ assert unreachable and not unreachable[0]["ok"]
+ assert unreachable[0]["role"] == "gpu_target"
+ assert "hint" in unreachable[0]
+
+
+def test_worker_layer_validates_runner_runtime_and_docker(tmp_path, monkeypatch):
+ plan, deployment = training_plan(tmp_path), service_deployment(tmp_path)
+ (tmp_path / "runs").mkdir()
+ token = tmp_path / "secrets" / "run-token"
+ token.parent.mkdir()
+ token.write_text("token\n")
+ token.chmod(0o600)
+ probe = json.dumps({"harbor_version": "0.23.0", "python": "3.12.13"})
+ run_ssh, _ = fake_remote({
+ "docker info": completed(returncode=1, stderr="Cannot connect to the Docker daemon"),
+ "cd ": completed(stdout=probe),
+ })
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ docker = [check for check in checks if check["check"] == "worker.docker"]
+ assert len(docker) == 2 and not any(check["ok"] for check in docker)
+ assert all("Docker daemon" in check["hint"] for check in docker)
+ harbor = [check for check in checks if check["check"] == "worker.harbor_version"]
+ assert all(check["ok"] for check in harbor)
+
+ run_ssh, _ = fake_remote({"cd ": completed(stdout=json.dumps(
+ {"harbor_version": "0.22.0", "python": "3.12.13"}))})
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ harbor = [check for check in checks if check["check"] == "worker.harbor_version"]
+ assert not any(check["ok"] for check in harbor)
+ # a wrong python version is also a failed runtime gate
+ run_ssh, _ = fake_remote({"cd ": completed(stdout=json.dumps(
+ {"harbor_version": "0.23.0", "python": "3.11.4"}))})
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = deployment_preflight.preflight(plan, deployment)
+ runtime = [check for check in checks if check["check"] == "worker.runner_runtime"]
+ assert not any(check["ok"] for check in runtime)
+
+
+def test_manager_layer_checks_storage_token_and_ports(tmp_path):
+ plan = training_plan(tmp_path)
+ deployment = resolvable(service_deployment(tmp_path))
+ checks = deployment_preflight.preflight(plan, deployment)
+ assert not by_name(checks, "storage.run_root")[0]["ok"] # run root missing
+
+ (tmp_path / "runs").mkdir()
+ token = tmp_path / "secrets" / "run-token"
+ token.parent.mkdir()
+ token.write_text("token\n")
+ token.chmod(0o644)
+ checks = deployment_preflight.preflight(plan, deployment)
+ assert not by_name(checks, "manager.run_token_file")[0]["ok"]
+ token.chmod(0o600)
+ checks = deployment_preflight.preflight(plan, deployment)
+ assert by_name(checks, "manager.run_token_file")[0]["ok"]
+ assert by_name(checks, "manager.control_port")[0]["ok"]
+
+
+def test_control_loop_layer_defers_live_dials_and_inference(tmp_path, monkeypatch):
+ plan, deployment = training_plan(tmp_path), service_deployment(tmp_path)
+ (tmp_path / "runs").mkdir()
+ token = tmp_path / "secrets" / "run-token"
+ token.parent.mkdir()
+ token.write_text("token\n")
+ token.chmod(0o600)
+ run_ssh, _ = fake_remote({})
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ monkeypatch.setattr(deployment_preflight.socket, "getaddrinfo",
+ lambda host, port: [(None, None, None, None, (host, port))])
+ checks = deployment_preflight.preflight(plan, deployment)
+ inference = by_name(checks, "connectivity.manager_to_gpu_inference")
+ assert inference and inference[0]["ok"] and "deferred" in inference[0]["detail"]
+ dials = [check for check in checks if check["check"].endswith(".dial")]
+ assert dials and all("deferred" not in check["detail"] or check["ok"] for check in dials)
+
+
+def test_doctor_gates_local_checks_on_gpu_transport(tmp_path, monkeypatch):
+ from harborrl.platform import preflight as platform_preflight
+
+ plan = training_plan(tmp_path)
+ plan["deployment"] = {"config": service_deployment(tmp_path),
+ "source": "deployment.yaml"}
+ run_ssh, _ = fake_remote({"nvidia-smi": completed(stdout="0\n" * 8)})
+ monkeypatch.setattr(platform_preflight, "_deployment",
+ lambda p: p.get("deployment", {}).get("config"))
+ monkeypatch.setattr(deployment_preflight.remote, "run_ssh", run_ssh)
+ checks = platform_preflight.doctor(plan)
+ # Local GPU dependency probes do not run for a remote GPU target.
+ assert not any(check.get("dependency") == "ray" for check in checks)
+ assert not any(check.get("resource") == "visible GPU budget" for check in checks)
+ assert not any(check.get("service") == "external Harbor worker" for check in checks)
+ assert any(check.get("role") == "gpu_target" for check in checks)
+
+ embedded = {"config": embedded_local(run_root=str(tmp_path / "runs")),
+ "source": "deployment.yaml"}
+ plan["deployment"] = embedded
+ checks = platform_preflight.doctor(plan)
+ assert any(check.get("dependency") == "ray" for check in checks)
+ assert any(check.get("resource") == "visible GPU budget" for check in checks)
+
+ # The auto-derived embedded-local topology keeps the legacy check set.
+ plan["deployment"] = {"config": embedded_local(), "source": "embedded-local"}
+ legacy = platform_preflight.doctor(plan)
+ assert not any(check.get("role") == "gpu_target" for check in legacy)
+ assert any(check.get("dependency") == "ray" for check in legacy)
diff --git a/tests/harborrl/test_deployment_targets.py b/tests/harborrl/test_deployment_targets.py
new file mode 100644
index 000000000..ef609d9ad
--- /dev/null
+++ b/tests/harborrl/test_deployment_targets.py
@@ -0,0 +1,235 @@
+"""TrainingTarget (P1): local child process and SSH launcher over shared storage."""
+from __future__ import annotations
+
+import json
+import subprocess
+import time
+
+import pytest
+
+from harborrl.deployment import training_target
+from harborrl.deployment.training_target import (
+ LocalTrainingTarget,
+ SshTrainingTarget,
+ TrainingTargetError,
+)
+
+
+def completed(returncode=0, stdout="", stderr=""):
+ return subprocess.CompletedProcess([], returncode, stdout, stderr)
+
+
+def test_local_target_runs_and_forwards_exit_code(tmp_path):
+ target = LocalTrainingTarget(["bash", "-c", "exit 3"], cwd=str(tmp_path), env={"PATH": "/bin"})
+ target.launch()
+ assert target.wait() == 3
+ assert target.query()["state"] == "exited"
+
+
+def test_local_target_cancel_interrupts(tmp_path):
+ target = LocalTrainingTarget(["bash", "-c", "trap '' INT; sleep 30"],
+ cwd=str(tmp_path), env={"PATH": "/bin"})
+ target.launch()
+ time.sleep(0.2)
+ assert target.query()["state"] == "running"
+ target.cancel()
+ with pytest.raises(subprocess.TimeoutExpired):
+ target.child.wait(timeout=2)
+ import os
+ import signal
+ os.killpg(os.getpgid(target.child.pid), signal.SIGKILL)
+ target.child.wait()
+
+
+def test_ssh_launcher_quotes_environment_and_captures_exit_code(tmp_path):
+ run_dir = tmp_path / "run"
+ run_dir.mkdir()
+ target = SshTrainingTarget({"host": "gpu"}, source_dir="/gpu/source", run_dir=run_dir)
+ launcher = target.write_launcher(
+ ["/manager/source/harborrl/platform/slime_train.sh", "--flag", "value with spaces"],
+ {"LR": "1e-6", "NOTE": "two spaces"},
+ path_rewrites=[("/manager/source", "/gpu/source")])
+ text = launcher.read_text()
+ assert "cd /gpu/source" in text
+ assert "/gpu/source/harborrl/platform/slime_train.sh" in text
+ assert "/manager/source" not in text
+ assert "export LR=1e-6" in text
+ assert "export NOTE='two spaces'" in text
+ assert "'value with spaces'" in text
+ assert "exit-code" in text
+ assert launcher.stat().st_mode & 0o111, "launcher must be executable"
+
+
+def test_ssh_target_environment_file_is_loaded_via_python(tmp_path):
+ run_dir = tmp_path / "run"
+ run_dir.mkdir()
+ environment_file = tmp_path / "gpu-env.json"
+ environment_file.write_text(json.dumps({"SLIME_DIR": "/opt/slime"}))
+ target = SshTrainingTarget({"host": "gpu"}, source_dir="/gpu/source", run_dir=run_dir,
+ environment_file=environment_file)
+ launcher = target.write_launcher(["true"], {})
+ text = launcher.read_text()
+ assert "HARBORRL_ENV_JSON" in text
+ assert "json.load" in text
+
+
+def test_ssh_target_lifecycle_with_fake_remote(tmp_path, monkeypatch):
+ run_dir = tmp_path / "run"
+ run_dir.mkdir()
+ target = SshTrainingTarget({"host": "gpu", "port": 2222}, source_dir="/gpu/source",
+ run_dir=run_dir, poll_interval=0.05)
+ calls = []
+ killed = {"done": False}
+
+ def fake_run_ssh(spec, command, *, timeout=30, connect_timeout=15):
+ calls.append((dict(spec), command))
+ if "setsid" in command:
+ (run_dir / "launcher.pid").write_text("4242\n")
+ return completed()
+ if "kill -0" in command:
+ return (completed(stdout="ok\n") if not killed["done"]
+ else completed(returncode=1))
+ if command.startswith("kill -INT"):
+ killed["done"] = True
+ (run_dir / "exit-code").write_text("130\n")
+ return completed()
+ return completed()
+
+ monkeypatch.setattr(training_target.remote, "run_ssh", fake_run_ssh)
+ target.write_launcher(["true"], {})
+ target.launch()
+ with pytest.raises(TrainingTargetError):
+ target.launch() # double launch is refused
+ assert target.query() == {"state": "running", "exit_code": None, "pid": 4242,
+ "run_dir": str(run_dir)}
+ assert target.cancel()
+ assert target.wait() == 130
+ assert target.query()["exit_code"] == 130
+ # The ssh destination honors user/port from the normalized spec.
+ assert any(spec.get("port") == 2222 for spec, _ in calls), (
+ "spec with port 2222 must reach run_ssh")
+ assert any("setsid" in command for _, command in calls)
+
+
+def test_ssh_target_wait_timeout(tmp_path, monkeypatch):
+ run_dir = tmp_path / "run"
+ run_dir.mkdir()
+ target = SshTrainingTarget({"host": "gpu"}, source_dir="/gpu/source", run_dir=run_dir,
+ poll_interval=0.05)
+
+ def fake_run_ssh(spec, command, *, timeout=30, connect_timeout=15):
+ if "setsid" in command:
+ (run_dir / "launcher.pid").write_text("99\n")
+ return completed()
+
+ monkeypatch.setattr(training_target.remote, "run_ssh", fake_run_ssh)
+ target.write_launcher(["true"], {})
+ target.launch()
+ with pytest.raises(TimeoutError):
+ target.wait(timeout=0.2)
+
+
+def test_ssh_command_assembles_noninteractive_ssh():
+ from harborrl.deployment.remote import ssh_command
+
+ argv = ssh_command({"host": "gpu", "user": "root", "port": 2222,
+ "identity": "/key"}, "true", connect_timeout=7)
+ assert argv[0] == "ssh"
+ assert argv[1:7] == ["-o", "BatchMode=yes", "-o", "ClearAllForwardings=yes",
+ "-o", "StrictHostKeyChecking=accept-new"]
+ assert argv[7:9] == ["-o", "ConnectTimeout=7"]
+ assert argv[9:13] == ["-p", "2222", "-i", "/key"]
+ assert argv[-2:] == ["root@gpu", "true"]
+
+
+def test_service_dispatch_launches_gpu_job_through_controller(tmp_path, monkeypatch, capsys):
+ """P1+P3 manager flow: materialize, register the run, launch, wait, report."""
+ import json as json_module
+ from pathlib import Path
+
+ from test_deployment_config import deployment_mapping
+ from test_native_training import native_config
+
+ from harborrl.config import launch_plan, load_config
+ from harborrl.data.harbor.native_inspector import inspect_native
+ from harborrl.deployment import controller as controller_module
+ from harborrl.deployment.schema import summary, validate
+
+ config_path, task = native_config(tmp_path)
+ catalog_path = Path(json_module.loads(
+ config_path.read_text().split("tasks: ", 1)[1].split("\n", 1)[0])["catalog"])
+ catalog = json_module.loads(catalog_path.read_text())
+ catalog[0]["task_digest"] = inspect_native(task)["task_digest"]
+ catalog_path.write_text(json_module.dumps(catalog))
+ config = load_config(config_path)
+ config["output"]["root"] = str(tmp_path / "shared-runs")
+ plan = launch_plan(config)
+
+ deployment = validate(deployment_mapping(tmp_path, storage={
+ "run_root": str(tmp_path / "shared-runs")}))
+ manager = deployment["manager"]
+ token_file = Path(manager["run_token_file"])
+ token_file.parent.mkdir(parents=True, exist_ok=True)
+ token_file.write_text("token\n")
+ token_file.chmod(0o600)
+ (tmp_path / "shared-runs").mkdir()
+ plan["deployment"] = summary(deployment, "deployment.yaml")
+
+ launched = {}
+
+ class FakeControl:
+ def __init__(self, service, host, port):
+ self.address = (host, port)
+
+ def serve_forever(self):
+ pass
+
+ def shutdown(self):
+ pass
+
+ def server_close(self):
+ pass
+
+ class FakeTarget:
+ def __init__(self, spec, *, source_dir, run_dir, **kwargs):
+ self.source_dir, self.run_dir = source_dir, Path(run_dir)
+
+ def write_launcher(self, command, environment, *, path_rewrites=()):
+ launched["command"] = list(command)
+ launched["environment"] = dict(environment)
+ launched["rewrites"] = list(path_rewrites)
+ return self.run_dir / "launcher.sh"
+
+ def launch(self):
+ launched["run_dir"] = str(self.run_dir)
+ (self.run_dir / "exit-code").write_text("0\n")
+
+ def wait(self):
+ return 0
+
+ def cancel(self):
+ return True
+
+ monkeypatch.setattr(controller_module, "make_control_server", FakeControl)
+ monkeypatch.setattr(controller_module, "SshTrainingTarget", FakeTarget)
+ monkeypatch.setattr(controller_module, 'check_gpu_control', lambda _: {'gpu_to_controller': True})
+
+ exit_code = controller_module.dispatch_service(plan, deployment, checks=[])
+ assert exit_code == 0
+ run_dir = Path(launched["run_dir"])
+ assert run_dir.is_dir()
+ environment = launched["environment"]
+ assert environment["HARBORRL_ROLLOUT_MODE"] == "service"
+ assert environment["HARBORRL_ROLLOUT_SERVICE_URL"] == manager["control_url"]
+ assert environment['HARBORRL_ROLLOUT_SERVICE_TOKEN_FILE'] == str(token_file)
+ assert 'HARBORRL_ROLLOUT_SERVICE_TOKEN' not in environment
+ assert environment["HARBORRL_NATIVE_GATEWAY_URL"] == manager["gateway_url"]
+ assert json_module.loads(environment["HARBORRL_NATIVE_RUNNER_WORKERS"]) == [
+ "worker-a", "worker-b"]
+ # The launcher runs inside the GPU checkout with a rewritten script path.
+ assert launched["rewrites"] == [(manager["source_dir"],
+ deployment["gpu_target"]["source_dir"])]
+ launch_record = json_module.loads((run_dir / "launch.json").read_text())
+ assert launch_record["control_url"] == manager["control_url"]
+ effective = json_module.loads((run_dir / "effective-config.json").read_text())
+ assert effective["components"]["execution_backend"] == "harbor_job"
diff --git a/tests/harborrl/test_math_interfaces.py b/tests/harborrl/test_math_interfaces.py
new file mode 100644
index 000000000..a17788fc1
--- /dev/null
+++ b/tests/harborrl/test_math_interfaces.py
@@ -0,0 +1,126 @@
+"""CPU contracts for the explicit MATH component interfaces."""
+import json
+from dataclasses import replace
+
+import pytest
+
+from harborrl.math_registry import (
+ BACKEND_CONTRACT,
+ list_components,
+ select_math,
+ validate_combination,
+)
+from harborrl.models.qwen import QwenModelAdapter
+from harborrl.tasks.jsonl_provider import JsonlTaskProvider
+from harborrl.tasks.provider import NativeTaskProvider
+
+
+def math_config():
+ return {
+ "execution": {"backend": "harbor_job"},
+ "harness": {"name": "claude_code"},
+ "harbor": {"version": "0.23.0"},
+ "gateway": {"audited_raw_logprobs": True},
+ "model": {"args_file": "qwen3-8B"},
+ "training": {"backend_contract": BACKEND_CONTRACT},
+ "sampling": {"group_size": 2},
+ }
+
+
+def test_registry_lists_versioned_default_components():
+ components = list_components()
+ assert [spec["id"] for spec in components["models"]] == ["qwen"]
+ assert [spec["id"] for spec in components["algorithms"]] == ["grpo"]
+ assert [spec["id"] for spec in components["tasks"]] == ["harbor_jsonl", "native_harbor"]
+ assert [spec["id"] for spec in components["harnesses"]] == ["claude_code"]
+ for dimension in components.values():
+ for spec in dimension:
+ assert spec["version"]
+ assert spec["evidence"]
+ assert spec["limitations"]
+
+def test_combination_checks_reject_incompatible_evidence_and_groups():
+ config = math_config()
+ selection = select_math(config)
+ assert validate_combination(selection, config) is selection
+
+ unaudited = {**config, "gateway": {"audited_raw_logprobs": False}}
+ with pytest.raises(ValueError, match="requires audited raw logprobs"):
+ validate_combination(selection, unaudited)
+
+ larger_group = replace(selection.algorithm, minimum_group_size=3)
+ with pytest.raises(ValueError, match="requires group size"):
+ validate_combination(replace(selection, algorithm=larger_group), config)
+
+ class Tokenizer:
+ def get(self, key):
+ assert key == "input_ids"
+ return [1, 2]
+
+ def apply_chat_template(self, *args, **kwargs):
+ assert kwargs["enable_thinking"] is False
+ return self
+
+ adapter = QwenModelAdapter()
+ converted = {"messages": [{"role": "user", "content": "run"}], "tools": []}
+ assert adapter.render_chat(Tokenizer(), converted) == [1, 2]
+ blocks = adapter.parse_output(
+ '{"name":"bash","arguments":{"command":"pwd"}} ',
+ response_id="message",
+ tools=[{"function": {"name": "bash"}}],
+ )
+ assert blocks[0]["type"] == "tool_use"
+
+
+def test_jsonl_task_provider_is_selected_by_catalog_format(tmp_path):
+ task = tmp_path / "task"
+ (task / "environment").mkdir(parents=True)
+ (task / "tests").mkdir()
+ (task / "task.toml").write_text('version = "1.0"\n')
+ (task / "instruction.md").write_text("jsonl instruction\n")
+ (task / "environment/Dockerfile").write_text("FROM debian:bookworm\n")
+ (task / "tests/test.sh").write_text("true\n")
+ provider = JsonlTaskProvider()
+ digest = provider.inspect(task)["task_digest"]
+ catalog = tmp_path / "catalog.jsonl"
+ catalog.write_text(json.dumps({
+ "id": "task", "revision": "1", "path": str(task),
+ "task_digest": digest, "reward_profile": {"key": "reward"},
+ }) + "\n")
+ config = {**math_config(), "tasks": {"catalog": str(catalog)}}
+ selection = select_math(config)
+ assert selection.task.id == "harbor_jsonl"
+ entries = provider.catalog(config)
+ rows = provider.prompt_rows(entries)
+ assert rows[0]["task"] == "jsonl instruction\n"
+ assert rows[0]["metadata"]["task_provider"] == "harbor_jsonl"
+
+ incompatible = {**config, "execution": {"backend": "process"}}
+ with pytest.raises(ValueError, match="does not support execution backend"):
+ validate_combination(select_math(incompatible), incompatible)
+
+ catalog.write_text("{broken\n")
+ with pytest.raises(ValueError, match="invalid JSONL catalog line"):
+ provider.catalog(config)
+
+
+def test_native_task_provider_locks_and_materializes_prompts(tmp_path):
+ task = tmp_path / "task"
+ (task / "environment").mkdir(parents=True)
+ (task / "tests").mkdir()
+ (task / "task.toml").write_text('version = "1.0"\n')
+ (task / "instruction.md").write_text("original instruction\n")
+ (task / "environment/Dockerfile").write_text("FROM debian:bookworm\n")
+ (task / "tests/test.sh").write_text("true\n")
+ provider = NativeTaskProvider()
+ digest = provider.inspect(task)["task_digest"]
+ catalog = tmp_path / "catalog.json"
+ catalog.write_text(json.dumps([{
+ "id": "task", "revision": "1", "path": str(task),
+ "task_digest": digest, "reward_profile": {"key": "reward"},
+ }]))
+ config = {**math_config(), "tasks": {"catalog": str(catalog)}}
+ entries = provider.catalog(config)
+ rows = provider.prompt_rows(entries)
+ assert rows[0]["task"] == "original instruction\n"
+ assert rows[0]["metadata"]["native_task"]["task_digest"] == digest
diff --git a/tests/harborrl/test_native_gateway.py b/tests/harborrl/test_native_gateway.py
index d3a6d5ffb..75ab7416e 100644
--- a/tests/harborrl/test_native_gateway.py
+++ b/tests/harborrl/test_native_gateway.py
@@ -65,9 +65,10 @@ def test_tool_roundtrip():
convert(p, model="policy")
-def test_claude_2_1_220_control_turns_are_accepted_without_hidden_sampling():
+@pytest.mark.parametrize("effort", ["high", "xhigh"])
+def test_claude_2_1_control_turns_are_accepted_without_hidden_sampling(effort):
converted = convert(payload(
- output_config={"effort": "high"},
+ output_config={"effort": effort},
thinking={"type": "adaptive"},
context_management={"edits": [{"type": "clear_thinking_20251015", "keep": "all"}]},
system=[{"type": "text", "text": "locked system prompt"}],
@@ -239,6 +240,32 @@ def test_nonfinite_generated_tool_is_rejected():
response_id="m", tools=[{"function": {"name": "bash"}}])
+def test_generated_tool_errors_distinguish_name_and_arguments():
+ tools = [{"function": {"name": "bash"}}]
+ with pytest.raises(ProtocolError, match="unknown generated tool"):
+ parse_qwen_output('{"name":"sh","arguments":{}} ',
+ response_id="m", tools=tools)
+ with pytest.raises(ProtocolError, match="arguments are not an object"):
+ parse_qwen_output('{"name":"bash","arguments":[]} ',
+ response_id="m", tools=tools)
+
+
+def test_generated_tool_name_alias_is_canonicalized_only_when_unique():
+ tools = [{"function": {"name": "Bash"}}]
+ output = parse_qwen_output('{"name":"bash","arguments":{"command":"pwd"}} ',
+ response_id="m", tools=tools)
+ assert output[0]["name"] == "Bash"
+ with pytest.raises(ProtocolError, match="unknown generated tool"):
+ parse_qwen_output('{"name":"sh","arguments":{}} ',
+ response_id="m", tools=tools)
+
+
+def test_qwen_finish_marker_maps_to_empty_end_turn():
+ output = parse_qwen_output('{"name":"finish","arguments":{}} ',
+ response_id="m", tools=[{"function": {"name": "Bash"}}])
+ assert output == []
+
+
def test_sglang_generation_version_and_logprob_evidence():
from harborrl.gateway.sglang_backend import SGLangBackend
class Tokenizer:
@@ -246,7 +273,11 @@ def apply_chat_template(self, *args, **kwargs):
return [11, 12]
def decode(self, token_ids, skip_special_tokens):
return "done"
- backend = SGLangBackend("http://engine", Tokenizer(), tokenizer_digest="tok", template_digest="tpl")
+ from harborrl.models.qwen import QwenModelAdapter
+ backend = SGLangBackend(
+ "http://engine", Tokenizer(), QwenModelAdapter(),
+ tokenizer_digest="tok", template_digest="tpl",
+ )
converted = convert(payload(), model="policy")
serving = {"input_ids": backend.prepare(converted)}
output = {"text": "done", "meta_info": {"weight_version": "1", "output_token_logprobs": [[-.2, 13]],
@@ -293,7 +324,31 @@ def open(self, request, timeout):
assert timeout == 30
return Response()
- backend = SGLangBackend("http://engine", SimpleNamespace(), tokenizer_digest="tok",
- template_digest="tpl", timeout=30)
+ from harborrl.models.qwen import QwenModelAdapter
+ backend = SGLangBackend(
+ "http://engine", SimpleNamespace(), QwenModelAdapter(),
+ tokenizer_digest="tok", template_digest="tpl", timeout=30,
+ )
backend.opener = Opener()
assert backend.weight_version() == "7"
+
+
+def test_native_gateway_thread_does_not_own_asyncio_default_executor():
+ from harborrl.rollout.native_generate import NativeRolloutRuntime
+
+ started, stopped = threading.Event(), threading.Event()
+
+ class Server:
+ def serve_forever(self):
+ started.set()
+ stopped.wait(5)
+
+ runtime = object.__new__(NativeRolloutRuntime)
+ runtime.server = Server()
+ runtime._start_gateway()
+ try:
+ assert started.wait(1)
+ assert runtime.thread.daemon is True
+ finally:
+ stopped.set()
+ runtime.thread.join(1)
diff --git a/tests/harborrl/test_native_lifecycle.py b/tests/harborrl/test_native_lifecycle.py
index bdc76b9ea..a36ef0fd9 100644
--- a/tests/harborrl/test_native_lifecycle.py
+++ b/tests/harborrl/test_native_lifecycle.py
@@ -35,7 +35,12 @@ def test_restart_cannot_reopen_persisted_attempt(tmp_path):
def test_profile_controls_reach_agent_and_host():
config = claude_config(PROFILE, "http://gateway:8000", "token")
+ assert config["kwargs"]["disallowed_tools"] == "Agent,Task"
+ disabled = claude_config({**PROFILE, "disallowed_tools": "Agent,Task,WebSearch,WebFetch"},
+ "http://gateway:8000", "token")
+ assert disabled["kwargs"]["disallowed_tools"] == "Agent,Task,WebSearch,WebFetch"
assert config["env"]["DISABLE_AUTO_COMPACT"] == "1"
+ assert config["env"]["ANTHROPIC_MODEL"] == "policy"
assert config["env"]["NO_PROXY"] == "gateway"
assert config["env"]["no_proxy"] == "gateway"
assert config["kwargs"]["version"] == "2.1.0"
diff --git a/tests/harborrl/test_native_terminal.py b/tests/harborrl/test_native_terminal.py
new file mode 100644
index 000000000..eb94c8a98
--- /dev/null
+++ b/tests/harborrl/test_native_terminal.py
@@ -0,0 +1,111 @@
+"""Regression coverage for the full coordinator success path and empty finals."""
+import asyncio
+import json
+import sys
+
+import pytest
+from test_native_contracts import identity, make_ir
+from test_native_lifecycle import PROFILE
+
+from harborrl.gateway.trace import TraceRegistry
+from harborrl.harnesses.claude_code import ClaudeCodeHarnessAdapter
+from harborrl.rollout.harbor_job.coordinator import run_attempt
+from harborrl.trajectories.native import RewardProfile, digest
+
+
+def test_casefold_tool_alias_must_be_unique():
+ from harborrl.gateway.messages import ProtocolError, parse_qwen_output
+ with pytest.raises(ProtocolError, match="ambiguous"):
+ parse_qwen_output('{"name":"BASH","arguments":{}} ',
+ response_id="r", tools=[{"function": {"name": "Bash"}},
+ {"function": {"name": "bash"}}])
+
+
+@pytest.mark.parametrize("empty", [False, True])
+def test_coordinator_publishes_complete_success(tmp_path, monkeypatch, empty):
+ ident = identity()
+ trial_id = "trial-" + ident.attempt_id
+ root = tmp_path / "attempt"
+ trial = root / "runner/harbor/trial"
+ template = make_ir(tmp_path / "fixture", nturns=2)
+
+ class Registry(TraceRegistry):
+ def register(self, ident, trial_id, **kwargs):
+ token = super().register(ident, trial_id, **kwargs)
+ turns = template["turns"]
+ for index, original in enumerate(turns):
+ aid, request, _, _ = self.begin(token)
+ turn = {**original, "request_id": request, "delivery": "sent"}
+ if empty and index == 1:
+ turn["response"] = {"content": []}
+ turn["response_digest"] = digest(turn["response"])
+ self.finish(aid, request, turn)
+ self.delivery(aid, turn["response_id"], "sent")
+ session = trial / "agent/sessions/projects/session.jsonl"
+ session.parent.mkdir(parents=True)
+ session.write_text("\n".join(json.dumps({
+ "type": "assistant", "sessionId": "session", "message": {
+ "id": turn["response_id"], "role": "assistant",
+ "content": turn["response"]["content"]}})
+ for turn in turns[:1 if empty else 2]))
+ (trial / "agent/claude-code.txt").write_text(json.dumps({
+ "type": "result", "subtype": "success", "is_error": False,
+ "result": "", "stop_reason": "end_turn", "session_id": "session",
+ "terminal_reason": "completed"}))
+ (trial / "verifier").mkdir()
+ (trial / "verifier/reward.json").write_text('{"reward": 1}')
+ (trial / "result.json").write_text(json.dumps({
+ "id": trial_id, "finished_at": "2026-10-19T00:00:00Z",
+ "verifier_environment_mode": "shared",
+ "verifier_result": {"rewards": {"reward": 1}}}))
+ return token
+
+ worker = tmp_path / "worker.py"
+ terminal = {"type": "terminal", "trial_id": trial_id,
+ "trial_dir": str(trial), "result_path": str(trial / "result.json")}
+ worker.write_text("import json, sys\njson.loads(sys.stdin.readline())\n"
+ f"print({json.dumps({'type': 'prepared', 'trial_id': trial_id})!r}, flush=True)\n"
+ "sys.stdin.readline()\n"
+ f"print({json.dumps(terminal)!r}, flush=True)\n")
+ spawn = asyncio.create_subprocess_exec
+
+ async def fake_spawn(*args, **kwargs):
+ return await spawn(sys.executable, "-S", str(worker), **kwargs)
+
+ monkeypatch.setattr(asyncio, "create_subprocess_exec", fake_spawn)
+ ir = asyncio.run(run_attempt(
+ ident, task_path=tmp_path, profile=PROFILE, gateway_url="http://gateway",
+ runner_python=sys.executable, root=root, registry=Registry(tmp_path / "trace"),
+ reward_profile=RewardProfile()))
+ assert ir["readiness"] == "CONTRACT_READY"
+ assert json.loads((root / "trajectory.v2.json").read_text()) == ir
+ assert (root / "evaluation.json").is_file()
+ assert not (root / "cleanup-required.json").exists()
+ assert all(turn["delivery"] == "consumed_confirmed" for turn in ir["turns"])
+
+
+@pytest.mark.parametrize("change", [
+ {"content": [{"type": "text", "text": "missing"}]},
+ {"finish_reason": "length"}, {"is_final": False},
+])
+def test_empty_final_rejects_other_missing_responses(tmp_path, change):
+ class Registry:
+ def unconsumed_responses(self, aid):
+ return [{"response_id": "r", "content": [], "finish_reason": "end_turn",
+ "is_final": True, **change}]
+ with pytest.raises(ValueError, match="only one empty final"):
+ ClaudeCodeHarnessAdapter().audit_empty_final(tmp_path / "absent", Registry(), "a", {})
+
+
+@pytest.mark.parametrize("change", [{"session_id": "wrong"}, {"is_error": True},
+ {"subtype": "error_max_turns"}, {"result": "nonempty"}, {"stop_reason": "max_tokens"}])
+def test_empty_final_requires_cli_consumption_evidence(tmp_path, change):
+ class Registry:
+ def unconsumed_responses(self, aid):
+ return [{"response_id": "r", "content": [], "finish_reason": "end_turn", "is_final": True}]
+ path = tmp_path / "claude-code.txt"
+ path.write_text(json.dumps({"type": "result", "subtype": "success", "is_error": False,
+ "result": "", "stop_reason": "end_turn", "session_id": "session",
+ "terminal_reason": "completed", **change}))
+ with pytest.raises(ValueError, match="matching session result"):
+ ClaudeCodeHarnessAdapter().audit_empty_final(path, Registry(), "a", {"session_id": "session"})
diff --git a/tests/harborrl/test_native_training.py b/tests/harborrl/test_native_training.py
index 40ce2b565..ecfd6a24e 100644
--- a/tests/harborrl/test_native_training.py
+++ b/tests/harborrl/test_native_training.py
@@ -142,8 +142,19 @@ def test_cli_command_mode_selects_experiment_dimensions(tmp_path, capsys, monkey
assert positional_plan["config"]["schema_version"] == 2
-def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path):
- native_config(tmp_path)
+@pytest.mark.parametrize('overrides, expected_lr', [
+ ([], '0.0001'),
+ (['training.learning_rate=0.000025'], '2.5e-05'),
+])
+def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path, overrides, expected_lr):
+ config_path, task = native_config(tmp_path)
+ from harborrl.data.harbor.native_inspector import inspect_native
+ config = load_config(config_path, overrides)
+ catalog_path = Path(config['tasks']['catalog'])
+ entries = json.loads(catalog_path.read_text())
+ entries[0]['task_digest'] = inspect_native(task)['task_digest']
+ catalog_path.write_text(json.dumps(entries))
+ plan = launch_plan(config)
prompt = tmp_path / "native.jsonl"
prompt.write_text('{"task":"fixture","metadata":{"harborrl_native":true}}\n')
root = tmp_path / "run"
@@ -154,6 +165,7 @@ def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path):
(model_args / "qwen3-8B.sh").write_text("MODEL_ARGS=(--hidden-size 4096)\n")
env = {key: value for key, value in os.environ.items()
if key in {"PATH", "HOME", "USER", "LANG", "LC_ALL", "TMPDIR"}}
+ env.update(plan["environment"])
env.update({
"DRY_RUN": "1", "DATASET": "native", "ALGO": "grpo",
"HARNESS_OPTION": "native-claude-code", "HARBORRL_NATIVE_ROLLOUT": "1",
@@ -162,14 +174,16 @@ def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path):
"MODEL_TAG": "qwen3-8B", "NUM_GPUS": "8", "ACTOR_GPUS": "4",
"ROLLOUT_GPUS": "4", "TP_SIZE": "4", "ROLLOUT_NUM_GPUS_PER_ENGINE": "4",
"HARBORRL_GPU_LAYOUT": "split", "NUM_ROLLOUT": "1", "SAVE_INTERVAL": "1",
- "ROLLOUT_BATCH_SIZE": "1", "N_SAMPLES": "2", "RUN_DIR": str(root), "RUN_ID": "run",
+ "RUN_DIR": str(root), "RUN_ID": "run",
"RUNS_ROOT": str(tmp_path), "CKPT_ROOT": str(root / "checkpoints"),
"HARBORRL_NATIVE_MAX_ATTEMPTS": "2", "HARBORRL_NATIVE_DRAIN_TIMEOUT": "1",
"HARBORRL_NATIVE_RUNNER_WORKERS": '["worker-a", "worker-b"]',
"HARBORRL_NATIVE_GATEWAY_HOST": "0.0.0.0",
"HARBORRL_NATIVE_SLIME_CONTRACT": "slime-v032",
"SLIME_DIR": str(slime),
+ "TRAIN_PYTHON": sys.executable,
})
+ env['LR'] = plan['environment']['LR']
script = Path(__file__).resolve().parents[2] / "harborrl/platform/slime_train.sh"
output = tmp_path / "shell.log"
with output.open("w") as stream:
@@ -179,6 +193,24 @@ def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path):
assert result.returncode == 0, result.stdout
command = next(line for line in result.stdout.splitlines() if line.startswith("[dry-run] "))
assert "--loss-type custom_loss" in command
+ import shlex
+ arguments = shlex.split(command.removeprefix('[dry-run] '))
+ assert arguments[arguments.index('--lr') + 1] == expected_lr
+ expected_flags = {
+ '--num-rollout': '1', '--save-interval': '1',
+ '--rollout-batch-size': '1', '--n-samples-per-prompt': '2',
+ '--rollout-max-response-len': '128', '--rollout-max-context-len': '1024',
+ '--num-gpus-per-node': '8', '--actor-num-gpus-per-node': '4',
+ '--rollout-num-gpus': '4', '--tensor-model-parallel-size': '4',
+ '--rollout-num-gpus-per-engine': '4', '--hf-checkpoint': '/model',
+ '--ref-load': '/reference', '--prompt-data': str(prompt),
+ '--save': str(root / 'checkpoints' / 'run'),
+ }
+ for flag, value in expected_flags.items():
+ assert arguments[arguments.index(flag) + 1] == value
+ recorded = json.loads(next((root / 'dry-run-commands').glob('*.json')).read_text())
+ assert recorded['command'] == arguments
+ assert recorded['mode'] == 'dry_run'
for argument in (
"--rollout-function-path harborrl.backends.slime_v032.rollout.generate_rollout",
"--custom-convert-samples-to-train-data-path harborrl.backends.slime_v032.converter.convert_samples_to_train_data",
@@ -208,96 +240,57 @@ def test_native_shell_dry_run_uses_official_v032_hooks(tmp_path):
assert "--no-load-optim" in command
-def test_native_runtime_binds_gateway_with_registry_and_backend(tmp_path, monkeypatch):
- from harborrl.gateway import server as gateway_server
- from harborrl.gateway import sglang_backend, trace
- from harborrl.rollout import native_generate
+def test_slime_runtime_config_maps_args_and_environment(tmp_path, monkeypatch):
+ from harborrl.backends.slime_v032 import rollout as slime_rollout
+ from harborrl.rollout.runtime import RolloutRuntimeConfig
catalog = tmp_path / "catalog.json"
catalog.write_text("[]")
profile = tmp_path / "profile.json"
profile.write_text(json.dumps({"model": "policy"}))
- tokenizer = SimpleNamespace(
- apply_chat_template=lambda *args, **kwargs: [1, 2],
- get_vocab=lambda: {"token": 1},
- )
- tokenizer_digest, template_digest = native_generate.tokenizer_fingerprint(tokenizer)
environment = {
- "RUN_DIR": str(tmp_path), "HARBORRL_NATIVE_CATALOG": str(catalog),
+ "RUN_ID": "run-1", "RUN_DIR": str(tmp_path),
+ "HARBORRL_NATIVE_CATALOG": str(catalog),
"HARBORRL_NATIVE_PROFILE": str(profile),
- "HARBORRL_NATIVE_TOKENIZER_DIGEST": tokenizer_digest,
- "HARBORRL_NATIVE_TEMPLATE_DIGEST": template_digest,
"HARBORRL_NATIVE_GATEWAY_HOST": "127.0.0.1",
"HARBORRL_NATIVE_GATEWAY_PORT": "49183",
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:49183",
+ "HARBORRL_NATIVE_RUNNER_PYTHON": "/runner/python",
+ "HARBORRL_NATIVE_RUNNER_WORKERS": '["worker-a", "worker-b"]',
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": "0" * 64,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": "1" * 64,
+ "ROLLOUT_MAX_RESPONSE_LEN": "128", "ROLLOUT_MAX_CONTEXT_LEN": "1024",
}
for key, value in environment.items():
monkeypatch.setenv(key, value)
-
- calls = {}
-
- class FakeGateway:
- def __init__(self, registry, backend, *, model, max_output_tokens):
- calls["gateway"] = (registry, backend, model, max_output_tokens)
- calls["gateway_object"] = self
-
- class FakeServer:
- def serve_forever(self):
- pass
-
- def shutdown(self):
- pass
-
- def server_close(self):
- pass
-
- class FakeTraceRegistry:
- def __init__(self, path):
- calls["registry_path"] = path
-
- class FakeBackend:
- def __init__(self, *args, **kwargs):
- calls["backend_args"] = (args, kwargs)
-
- def weight_version(self):
- return "7"
-
- def fake_make_server(gateway, host, port):
- calls["server"] = (gateway, host, port)
- return FakeServer()
-
+ args = SimpleNamespace(rollout_num_gpus=8, rollout_num_gpus_per_engine=4,
+ sglang_server_concurrency=2, hf_checkpoint="/model",
+ sglang_router_ip="127.0.0.1", sglang_router_port=30000,
+ rollout_max_context_len=1024, rollout_max_response_len=128,
+ n_samples_per_prompt=4)
+ config = slime_rollout.runtime_config_from_args(args)
+ assert isinstance(config, RolloutRuntimeConfig)
+ assert config.endpoint == "http://127.0.0.1:30000"
+ assert config.concurrency_limit == 4 # 2 concurrency x 2 engines
+ assert config.group_size == 4
+ assert config.workers == ("worker-a", "worker-b")
+ assert config.max_output_tokens == 128
+
+ # The tokenizer loader keeps the single Slime dependency at the boundary.
slime = ModuleType("slime")
utils = ModuleType("slime.utils")
processing = ModuleType("slime.utils.processing_utils")
+ tokenizer = SimpleNamespace()
processing.load_tokenizer = lambda *args, **kwargs: tokenizer
slime.utils = utils
utils.processing_utils = processing
monkeypatch.setitem(sys.modules, "slime", slime)
monkeypatch.setitem(sys.modules, "slime.utils", utils)
monkeypatch.setitem(sys.modules, "slime.utils.processing_utils", processing)
- monkeypatch.setattr(gateway_server, "Gateway", FakeGateway)
- monkeypatch.setattr(gateway_server, "make_server", fake_make_server)
- monkeypatch.setattr(sglang_backend, "SGLangBackend", FakeBackend)
- monkeypatch.setattr(trace, "TraceRegistry", FakeTraceRegistry)
- monkeypatch.setattr(native_generate, "_RUNTIME", None)
-
- args = SimpleNamespace(rollout_num_gpus=4, rollout_num_gpus_per_engine=4,
- sglang_server_concurrency=1, hf_checkpoint="/model",
- sglang_router_ip="127.0.0.1", sglang_router_port=30000,
- rollout_max_context_len=1024, rollout_max_response_len=128)
- runtime = asyncio.run(native_generate.runtime(args))
- try:
- registry = calls["gateway"][0]
- backend = calls["gateway"][1]
- assert calls["gateway"] == (registry, backend, "policy", 128)
- assert calls["server"][0] is calls["gateway_object"]
- assert calls["server"][1:] == ("127.0.0.1", 49183)
- assert runtime.backend.weight_version() == "7"
- finally:
- runtime.close()
- native_generate._RUNTIME = None
+ assert slime_rollout.slime_tokenizer_loader(args)() is tokenizer
def test_native_policy_identity_uses_synchronized_pool_version(tmp_path, monkeypatch):
- from harborrl.rollout import native_generate
+ from harborrl.rollout import runtime as rollout_runtime
class Backend:
endpoint = "http://router:30000"
@@ -314,26 +307,37 @@ def publish_version(self, states, expected):
monkeypatch.setenv("RUN_DIR", str(tmp_path))
registry = Registry()
- runtime = SimpleNamespace(run_root=tmp_path, backend=Backend(), registry=registry)
- assert native_generate.current_policy_version(runtime) == "2"
+ runtime = SimpleNamespace(run_root=tmp_path, backend=Backend(), registry=registry,
+ config=SimpleNamespace(drain_timeout=30))
+ assert rollout_runtime.current_policy_version(runtime) == "2"
payload = json.loads((tmp_path / "policy-pool.json").read_text())
assert payload["weight_version"] == "2"
assert registry.published[1] == "2"
+ assert registry.timeout == 30
def test_native_slots_spread_workers_and_shift_retries(tmp_path, monkeypatch):
- from harborrl.rollout import native_generate
+ from harborrl.rollout import runtime as rollout_runtime
entry = {"id": "task", "revision": "1", "path": "/task", "task_digest": "d" * 64,
"reward_profile": {"key": "reward", "scale": 1.0, "offset": 0.0, "raw_range": [0.0, 1.0]}}
profile = tmp_path / "profile.json"
profile.write_text(json.dumps({"model": "policy"}))
- monkeypatch.setenv("RUN_ID", "run")
- monkeypatch.setenv("HARBORRL_NATIVE_PROFILE", str(profile))
- monkeypatch.setenv("HARBORRL_NATIVE_RUNNER_WORKERS", json.dumps(["worker-a", "worker-b"]))
- monkeypatch.setenv("HARBORRL_NATIVE_MAX_ATTEMPTS", "2")
- monkeypatch.setenv("HARBORRL_NATIVE_GATEWAY_URL", "http://gateway:32123")
- monkeypatch.setenv("HARBORRL_NATIVE_RUNNER_PYTHON", "/python")
+ environment = {
+ "RUN_ID": "run", "RUN_DIR": str(tmp_path), "HARBORRL_NATIVE_PROFILE": str(profile),
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(["worker-a", "worker-b"]),
+ "HARBORRL_NATIVE_MAX_ATTEMPTS": "2",
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:32123",
+ "HARBORRL_NATIVE_GATEWAY_PORT": "32123",
+ "HARBORRL_NATIVE_RUNNER_PYTHON": "/python",
+ "HARBORRL_NATIVE_CATALOG": str(tmp_path / "catalog.json"),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": "0" * 64,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": "1" * 64,
+ }
+ for key, value in environment.items():
+ monkeypatch.setenv(key, value)
+ config = rollout_runtime.RolloutRuntimeConfig.from_environment(
+ environment, endpoint="http://router:30000", concurrency_limit=8, group_size=4)
class FakeSlots:
max_attempts = 2
@@ -360,19 +364,15 @@ async def fake_run_attempt(identity, *, runner_host, **kwargs):
raise RuntimeError("simulated first-attempt failure")
return {"identity": identity.__dict__}
- monkeypatch.setattr(native_generate, "GroupSlots", fake_group_slots)
- monkeypatch.setattr(native_generate, "run_attempt", fake_run_attempt)
+ monkeypatch.setattr(rollout_runtime, "GroupSlots", fake_group_slots)
+ monkeypatch.setattr(rollout_runtime, "run_attempt", fake_run_attempt)
- args = SimpleNamespace(n_samples_per_prompt=4, rollout_max_response_len=64)
runtime_ = SimpleNamespace(rollout_root=tmp_path, semaphore=asyncio.Semaphore(8),
registry=SimpleNamespace(), profile={"model": "policy"})
- sample = SimpleNamespace(metadata={"native_task": entry}, group_index=0)
async def gather_slots():
return await asyncio.gather(*[
- native_generate._run_slot(
- args, runtime_, sample, slot, 0, entry, "sampling", "1"
- )
+ rollout_runtime.run_slot(config, runtime_, 0, slot, 0, entry, "sampling", "1")
for slot in range(4)])
results = asyncio.run(gather_slots())
@@ -413,13 +413,96 @@ def test_native_dispatch_passes_materialized_prompt_data(tmp_path, monkeypatch):
monkeypatch.setenv("SLIME_DIR", "/opt/slime-v0.3.2")
monkeypatch.setenv("MEGATRON_DIR", "/opt/megatron")
monkeypatch.setenv("SGLANG_IMAGE", "sglang:v0.5.15.post1-cu129")
+ monkeypatch.setenv("LR", "99")
+ monkeypatch.setenv("N_SAMPLES", "99")
+ monkeypatch.setenv("ANTHROPIC_API_KEY", "ambient-provider-credential")
+ monkeypatch.setenv("HTTPS_PROXY", "http://user:credential@proxy.example:8080")
monkeypatch.setattr(native_train, "doctor", lambda *_args, **_kwargs: [])
assert native_train.dispatch(plan) == 0
launch_path = next((tmp_path / "runs" / "training").glob("*/launch.json"))
launch = json.loads(launch_path.read_text())
assert launch["prompt_data"] == output.read_text()
+ effective_text = (launch_path.parent / "effective-config.json").read_text()
+ effective = json.loads(effective_text)
+ assert effective['environment']['LR'] == '0.0001'
+ assert effective['environment']['N_SAMPLES'] == '2'
+ assert effective['environment']['ROLLOUT_PROMPT_DATA'] == output.read_text()
+ assert effective['components']['algorithm'] == 'grpo'
+ assert effective['deployment_environment']['SLIME_DIR'] == '/opt/slime-v0.3.2'
+ assert 'ambient-provider-credential' not in effective_text
+ assert 'user:credential' not in effective_text
assert path_output.read_text().split(os.pathsep, 1)[0] == str(Path(plan["command"][0]).parent)
assert json.loads(workers_output.read_text()) == ["worker-a", "worker-b"]
assert gateway_output.read_text() == "gateway"
assert backend_output.read_text() == (
"/opt/slime-v0.3.2:/opt/megatron:sglang:v0.5.15.post1-cu129")
+
+
+def test_all_schema_fields_have_declared_consumers():
+ from harborrl.backends.slime_v032.config import field_usage
+ from harborrl.config.native import FIELDS
+ assert set(field_usage()) == {
+ f'{section}.{field}' for section, fields in FIELDS.items() for field in fields
+ }
+
+
+def test_schema_validation_does_not_read_harness_files(tmp_path):
+ from harborrl.config.native import validate, validate_files
+ config_path, _ = native_config(tmp_path)
+ config = load_config(config_path)
+ Path(config['harness']['profile']).unlink()
+ assert validate(config, config_path) is config
+ with pytest.raises(FileNotFoundError):
+ validate_files(config)
+
+
+@pytest.mark.parametrize('field, value, message', [
+ ('training', None, 'training'),
+ ('training.backend_contract', [], 'backend_contract'),
+ ('gateway.advertised_url', [], 'gateway.advertised_url'),
+ ('sampling.max_tokens', 'many', 'sampling.max_tokens'),
+])
+def test_invalid_config_types_are_cli_errors(tmp_path, capsys, field, value, message):
+ import yaml
+
+ from harborrl.cli import main
+ config_path, _ = native_config(tmp_path)
+ config = yaml.safe_load(config_path.read_text())
+ section, _, key = field.partition('.')
+ if key:
+ config[section][key] = value
+ else:
+ config[section] = value
+ config_path.write_text(yaml.safe_dump(config))
+ with pytest.raises(SystemExit) as exc:
+ main(['train', str(config_path), '--dry-run'])
+ assert exc.value.code == 2
+ assert message in capsys.readouterr().err
+
+
+def test_invalid_override_yaml_is_cli_error(tmp_path, capsys):
+ from harborrl.cli import main
+ config_path, _ = native_config(tmp_path)
+ with pytest.raises(SystemExit) as exc:
+ main(['train', str(config_path), '--set', 'training.learning_rate=[', '--dry-run'])
+ assert exc.value.code == 2
+ assert 'training.learning_rate: invalid YAML override' in capsys.readouterr().err
+
+
+def test_harness_file_override_is_checked_after_override(tmp_path):
+ config_path, _ = native_config(tmp_path)
+ config = load_config(config_path)
+ old_profile = Path(config['harness']['profile'])
+ new_profile = tmp_path / 'replacement.json'
+ old_profile.rename(new_profile)
+ config = load_config(config_path, [f'harness.profile={new_profile}'])
+ assert config['harness']['profile'] == str(new_profile)
+
+
+def test_catalog_rejects_nonmapping_entry(tmp_path):
+ from harborrl.tasks.catalog import catalog
+ config_path, _ = native_config(tmp_path)
+ config = load_config(config_path)
+ Path(config['tasks']['catalog']).write_text('[null]')
+ with pytest.raises(ValueError, match='catalog identity'):
+ catalog(config)
diff --git a/tests/harborrl/test_options.py b/tests/harborrl/test_options.py
new file mode 100644
index 000000000..aa3739c4b
--- /dev/null
+++ b/tests/harborrl/test_options.py
@@ -0,0 +1,308 @@
+"""Offline wizard contracts, including the existing training CLI boundary."""
+import json
+import subprocess
+import sys
+from copy import deepcopy
+from pathlib import Path
+
+import pytest
+import yaml
+from test_native_training import native_config
+
+from harborrl.options.catalog import TRAINING_FIELDS, build_catalog, dataset_metadata
+from harborrl.options.cli import main
+from harborrl.options.presets import PRESETS, worker_defaults
+from harborrl.options.validation import validate_paths
+from harborrl.options.wizard import build_config_bundle, publish_bundle
+
+
+@pytest.fixture
+def answers(tmp_path):
+ _path, task = native_config(tmp_path)
+ from harborrl.data.harbor.native_inspector import inspect_native
+ catalog = tmp_path / "catalog.json"
+ entries = json.loads(catalog.read_text())
+ entries[0]["task_digest"] = inspect_native(task)["task_digest"]
+ catalog.write_text(json.dumps(entries))
+ model = tmp_path / "model"
+ model.mkdir()
+ return {"answers_version": 1, "preset": "embedded-local", "training": {
+ "tasks": {"catalog": str(catalog)}, "harness": {"profile": str(tmp_path / "claude.json")},
+ "harbor": {"python": sys.executable},
+ "model": {"checkpoint": str(model), "reference": str(model)},
+ "gateway": {"tokenizer_digest": "1" * 64, "template_digest": "2" * 64},
+ }}
+
+
+def service_answers(answers, preset):
+ answers = deepcopy(answers)
+ answers["preset"] = preset
+ answers["deployment"] = {
+ "manager": {"control_url": "http://controller.example:49200", "gateway_url": "http://controller.example:49184"},
+ "gpu_target": {"ssh": {"host": "gpu", "user": "trainer", "port": 22},
+ "source_dir": "/remote/source", "train_python": "/remote/python"},
+ }
+ if preset == "service-ssh":
+ worker = worker_defaults()
+ worker["ssh"]["host"] = "runner"
+ answers["deployment"]["rollout"] = {"workers": [worker]}
+ del answers["training"]["harbor"]["python"]
+ else:
+ answers["sandbox_config"] = "/site/sandbox.yaml"
+ return answers
+
+
+def test_catalog_contract(monkeypatch):
+ def forbidden(*args, **kwargs):
+ raise AssertionError("offline operation attempted subprocess")
+ monkeypatch.setattr(subprocess, "run", forbidden)
+ monkeypatch.setattr(subprocess, "Popen", forbidden)
+ from harborrl.config.native import FIELDS
+ assert set(TRAINING_FIELDS) == {f"{s}.{f}" for s, fields in FIELDS.items() for f in fields}
+ first = build_catalog()
+ assert json.dumps(first, sort_keys=True) == json.dumps(build_catalog(), sort_keys=True)
+ assert all(not row["available"] for row in first["datasets"] if row["status"] == "planned")
+ from harborrl.config import ROOT
+ assert dataset_metadata() == yaml.safe_load((ROOT / "configs/harbor_hub/manifests.yaml").read_text())
+
+
+@pytest.mark.parametrize("dimension", [None, "models", "algorithms", "tasks", "harnesses", "datasets", "deployments", "fields", "compat"])
+def test_catalog_cli(dimension, capsys):
+ assert main(["options", *([dimension] if dimension else []), "--json"]) == 0
+ captured = capsys.readouterr()
+ result = json.loads(captured.out)
+ assert not captured.err
+ assert result["catalog_version"] == 1
+ if dimension:
+ assert len(result) == 3
+
+
+@pytest.mark.parametrize("preset", [p["id"] for p in PRESETS])
+def test_presets_and_legacy_dry_run(preset, answers, tmp_path, capsys):
+ if preset != "embedded-local":
+ answers = service_answers(answers, preset)
+ original = deepcopy(answers)
+ output = tmp_path / "new site"
+ bundle = build_config_bundle(answers, output_directory=output)
+ assert answers == original
+ assert not output.exists()
+ publish_bundle(bundle, output)
+ result = validate_paths(output / "config.yaml", output / "deployment.yaml")
+ assert result["status"] == "valid_with_deferred_checks", result
+ if preset != "service-sandbox-local-runner":
+ from harborrl.cli import main as legacy_main
+ assert legacy_main(["train", str(output / "config.yaml"), "--deployment", str(output / "deployment.yaml"), "--dry-run"]) == 0
+ assert json.loads(capsys.readouterr().out)["config"]["schema_version"] == 2
+
+
+def test_local_files_and_remote_ownership(answers, tmp_path):
+ for preset in ("embedded-local", "service-ssh"):
+ current = answers if preset == "embedded-local" else service_answers(answers, preset)
+ out = tmp_path / preset
+ publish_bundle(build_config_bundle(current, output_directory=out), out)
+ result = validate_paths(out / "config.yaml", out / "deployment.yaml", check_files=True)
+ assert result["status"] == "valid_with_deferred_checks", result
+ if preset == "service-ssh":
+ assert any(c["code"] == "remote_paths" for c in result["checks"])
+ (tmp_path / "claude.json").unlink()
+ assert validate_paths(out / "config.yaml", out / "deployment.yaml")["status"] != "invalid"
+ assert validate_paths(out / "config.yaml", out / "deployment.yaml", check_files=True)["status"] == "invalid"
+
+
+@pytest.mark.parametrize("mutation", ["unknown", "missing", "zero_digest", "gpu", "gateway", "provider"])
+def test_reject_bad_answers(mutation, answers, tmp_path):
+ answers = service_answers(answers, "service-ssh")
+ if mutation == "unknown":
+ answers["training"]["sampling"] = {"typo": 1}
+ elif mutation == "missing":
+ del answers["training"]["tasks"]
+ elif mutation == "zero_digest":
+ answers["training"]["gateway"]["tokenizer_digest"] = "0" * 64
+ elif mutation == "gpu":
+ answers["deployment"]["gpu_target"]["expected_gpus"] = 0
+ elif mutation == "gateway":
+ answers["training"]["gateway"]["advertised_url"] = "http://other.example:49184"
+ else:
+ answers["deployment"]["rollout"]["provider"] = "sandbox"
+ with pytest.raises(ValueError):
+ build_config_bundle(answers, output_directory=tmp_path / "output")
+ assert not (tmp_path / "output").exists()
+
+
+def test_noninteractive_and_failure_json(answers, tmp_path, monkeypatch, capsys):
+ monkeypatch.setattr(sys.stdin, "isatty", lambda: False)
+ with pytest.raises(SystemExit) as exc:
+ main(["init", "--out-dir", str(tmp_path / "output")])
+ assert exc.value.code == 2
+ capsys.readouterr()
+ assert main(["validate", str(tmp_path / "missing"), "--json"]) == 1
+ assert json.loads(capsys.readouterr().out)["status"] == "invalid"
+ file = tmp_path / "answers.yaml"
+ file.write_text(yaml.safe_dump(answers))
+ assert main(["init", "--answers", str(file), "--out-dir", str(tmp_path / "output")]) == 0
+ assert "harborrl train" in capsys.readouterr().out
+ assert main(["init", "--answers", str(file), "--out-dir", str(tmp_path / "output")]) == 1
+
+
+def test_publication_failure_lock_and_traversal(answers, tmp_path, monkeypatch):
+ output = tmp_path / "output"
+ bundle = build_config_bundle(answers, output_directory=output)
+ lock = tmp_path / ".output.init.lock"
+ lock.write_text("other process")
+ with pytest.raises(FileExistsError):
+ publish_bundle(bundle, output)
+ assert lock.read_text() == "other process"
+ lock.unlink()
+ bad = deepcopy(bundle)
+ bad["files"]["../escape"] = "bad"
+ with pytest.raises(ValueError):
+ publish_bundle(bad, output)
+ def fail(*args, **kwargs):
+ raise OSError("injected rename failure")
+ with monkeypatch.context() as context:
+ context.setattr(Path, "rename", fail)
+ with pytest.raises(OSError, match="injected"):
+ publish_bundle(bundle, output)
+ assert not output.exists() and not list(tmp_path.glob(".output.init*"))
+ publish_bundle(bundle, output)
+ before = (output / "config.yaml").read_bytes()
+ with pytest.raises(FileExistsError):
+ publish_bundle(bundle, output)
+ assert (output / "config.yaml").read_bytes() == before
+
+
+@pytest.mark.parametrize("confirmation", ["yes", "no"])
+def test_interactive(answers, tmp_path, monkeypatch, confirmation):
+ t = answers["training"]
+ responses = iter(["1", sys.executable, sys.executable, t["tasks"]["catalog"], t["harness"]["profile"],
+ t["model"]["checkpoint"], t["model"]["reference"], "", "1" * 64, "2" * 64,
+ "", "", "1", "", "", "", "", confirmation])
+ monkeypatch.setattr(sys.stdin, "isatty", lambda: True)
+ monkeypatch.setattr("builtins.input", lambda _: next(responses))
+ output = tmp_path / "interactive"
+ assert main(["init", "--out-dir", str(output)]) == 0
+ assert output.exists() == (confirmation == "yes")
+
+
+def test_probe_failure_and_alias_parsing(tmp_path, monkeypatch):
+ from harborrl.options.probe import probe_local, ssh_aliases
+ config = tmp_path / "ssh-config"
+ config.write_text("Host train runner *.example !excluded\n HostName host\nMatch exec unsafe\n User user\n")
+ assert ssh_aliases(config) == ["runner", "train"]
+ def timeout(*args, **kwargs):
+ assert kwargs["timeout"] == 3
+ raise subprocess.TimeoutExpired(args[0], 3)
+ monkeypatch.setattr(subprocess, "run", timeout)
+ result = probe_local(tmp_path / "output")
+ assert result["gpu"]["status"] == result["python"]["status"] == "unknown"
+ assert not (tmp_path / "output").exists()
+
+
+def test_sandbox_files_and_remote_runner(answers, tmp_path, capsys):
+ from harborrl.cli import main as legacy_main
+ from harborrl.data.harbor.native_inspector import inspect_native
+ task = tmp_path / "task"
+ (task / "task.toml").write_text('version = "1.0"\n[environment]\ndocker_image = "task:locked"\n')
+ catalog = tmp_path / "catalog.json"
+ entries = json.loads(catalog.read_text())
+ entries[0]["task_digest"] = inspect_native(task)["task_digest"]
+ catalog.write_text(json.dumps(entries))
+ sandbox = tmp_path / "sandbox.yaml"
+ sandbox.write_text(yaml.safe_dump({"domain": "https://sandbox.example", "credential_file": "/missing/secret",
+ "bindings": [{"image": "task:locked", "max_live_instances": 2}]}))
+ answers = service_answers(answers, "service-sandbox-local-runner")
+ answers["sandbox_config"] = str(sandbox)
+ for remote in (False, True):
+ if remote:
+ worker = worker_defaults()
+ worker["ssh"]["host"] = "runner"
+ worker["docker"] = False
+ answers["deployment"]["rollout"] = {"workers": [worker]}
+ answers["training"]["harbor"] = {"runner_transport": "ssh"}
+ output = tmp_path / f"sandbox-{remote}"
+ publish_bundle(build_config_bundle(answers, output_directory=output), output)
+ report = validate_paths(output / "config.yaml", output / "deployment.yaml", check_files=True)
+ assert report["status"] == "valid_with_deferred_checks", report
+ assert legacy_main(["train", str(output / "config.yaml"), "--deployment", str(output / "deployment.yaml"), "--dry-run"]) == 0
+ capsys.readouterr()
+ sandbox.write_text(sandbox.read_text().replace("task:locked", "other:locked"))
+ assert validate_paths(output / "config.yaml", output / "deployment.yaml", check_files=True)["status"] == "invalid"
+
+
+def test_offline_import_boundary(answers, tmp_path):
+ from harborrl.config import ROOT
+ output = tmp_path / "offline"
+ publish_bundle(build_config_bundle(answers, output_directory=output), output)
+ script = '''
+import builtins, socket, subprocess, sys
+sys.path.insert(0, sys.argv[1])
+original = builtins.__import__
+def guarded(name, *args, **kwargs):
+ assert name.split('.')[0] not in {'torch', 'transformers', 'sglang', 'harbor', 'opensandbox', 'httpx'}
+ return original(name, *args, **kwargs)
+def forbidden(*args, **kwargs):
+ raise AssertionError('offline command attempted an external operation')
+builtins.__import__ = guarded
+socket.socket = forbidden
+subprocess.run = subprocess.Popen = forbidden
+from harborrl.cli import main
+assert main(['options', '--json']) == 0
+assert main(['validate', sys.argv[2], '--deployment', sys.argv[3], '--check-files', '--json']) == 0
+'''
+ result = subprocess.run([sys.executable, "-I", "-c", script, str(ROOT), str(output / "config.yaml"),
+ str(output / "deployment.yaml")], capture_output=True, text=True, timeout=20, check=False)
+ assert result.returncode == 0, result.stderr
+
+
+def test_concurrent_publication(answers, tmp_path):
+ from concurrent.futures import ThreadPoolExecutor
+ from threading import Barrier
+ output = tmp_path / "concurrent"
+ bundle = build_config_bundle(answers, output_directory=output)
+ barrier = Barrier(2)
+ def attempt():
+ barrier.wait(timeout=10)
+ try:
+ publish_bundle(bundle, output)
+ except FileExistsError:
+ return False
+ return True
+ with ThreadPoolExecutor(max_workers=2) as pool:
+ assert sorted(pool.map(lambda _: attempt(), range(2))) == [False, True]
+ assert {p.name for p in output.iterdir()} == set(bundle["files"])
+
+
+def test_relative_paths_and_invalid_yaml(answers, tmp_path, capsys):
+ answers["training"]["tasks"]["catalog"] = "../catalog.json"
+ output = tmp_path / "relative"
+ publish_bundle(build_config_bundle(answers, output_directory=output), output)
+ config = yaml.safe_load((output / "config.yaml").read_text())
+ assert config["tasks"]["catalog"] == str(tmp_path / "catalog.json")
+ bad = tmp_path / "bad.yaml"
+ bad.write_text("[invalid")
+ assert main(["validate", str(bad), "--json"]) == 1
+ assert json.loads(capsys.readouterr().out)["status"] == "invalid"
+
+
+def test_embedded_explicit_conflict(answers, tmp_path):
+ answers["training"]["gateway"]["advertised_url"] = "http://localhost:49184"
+ answers["deployment"] = {"manager": {"gateway_url": "http://127.0.0.1:49184"}}
+ with pytest.raises(ValueError, match="conflicts"):
+ build_config_bundle(answers, output_directory=tmp_path / "conflict")
+
+
+def test_options_text_is_concise(capsys):
+ assert main(["options"]) == 0
+ output = capsys.readouterr().out
+ assert len(output.splitlines()) < 50
+ assert "locally_available=False" in output
+
+
+def test_public_answer_templates(tmp_path):
+ from harborrl.config import ROOT
+ for file in sorted((ROOT / "examples/options").glob("*.yaml")):
+ answers = yaml.safe_load(file.read_text())
+ with pytest.raises(ValueError, match="SHA-256"):
+ build_config_bundle(answers, output_directory=tmp_path / file.stem)
+ answers["training"]["gateway"].update(tokenizer_digest="1" * 64, template_digest="2" * 64)
+ assert build_config_bundle(answers, output_directory=tmp_path / file.stem)["validation"]["status"] != "invalid"
diff --git a/tests/harborrl/test_rollout_runtime.py b/tests/harborrl/test_rollout_runtime.py
new file mode 100644
index 000000000..8f33323c5
--- /dev/null
+++ b/tests/harborrl/test_rollout_runtime.py
@@ -0,0 +1,321 @@
+"""Extracted rollout core (P2): config mapping, slot scheduling, boundaries."""
+from __future__ import annotations
+
+import asyncio
+import json
+from pathlib import Path
+from types import SimpleNamespace
+
+import pytest
+
+
+def test_runtime_config_maps_environment(tmp_path, monkeypatch):
+ from harborrl.rollout.runtime import RolloutRuntimeConfig
+
+ environment = {
+ "RUN_ID": "run-1", "RUN_DIR": str(tmp_path),
+ "HARBORRL_NATIVE_CATALOG": str(tmp_path / "catalog.json"),
+ "HARBORRL_NATIVE_PROFILE": str(tmp_path / "profile.json"),
+ "HARBORRL_NATIVE_GATEWAY_HOST": "127.0.0.1",
+ "HARBORRL_NATIVE_GATEWAY_PORT": "49183",
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:49183",
+ "HARBORRL_NATIVE_RUNNER_PYTHON": "/runner/python",
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(["worker-a", "worker-b"]),
+ "HARBORRL_NATIVE_TASK_WORKER_OVERRIDES": json.dumps({"task": "worker-b"}),
+ "HARBORRL_NATIVE_MAX_ATTEMPTS": "3",
+ "ROLLOUT_MAX_RESPONSE_LEN": "128",
+ "ROLLOUT_MAX_CONTEXT_LEN": "1024",
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": "0" * 64,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": "1" * 64,
+ "HARBORRL_NATIVE_AUDITED_LOGPROBS": "1",
+ "HARBORRL_NATIVE_DRAIN_TIMEOUT": "5",
+ "MODEL_TAG": "qwen3-8B",
+ }
+ for key, value in environment.items():
+ monkeypatch.setenv(key, value)
+ config = RolloutRuntimeConfig.from_environment(
+ environment, endpoint="http://router:30000/", concurrency_limit=4, group_size=2)
+ assert config.workers == ("worker-a", "worker-b")
+ assert config.task_worker_overrides == {"task": "worker-b"}
+ assert config.max_attempts == 3 and config.max_output_tokens == 128
+ assert config.endpoint == "http://router:30000"
+ assert config.environment()["HARBORRL_NATIVE_RUNNER_WORKERS"] == \
+ json.dumps(["worker-a", "worker-b"])
+ with pytest.raises(ValueError, match="group size"):
+ RolloutRuntimeConfig.from_environment(environment, endpoint="x", concurrency_limit=1,
+ group_size=1)
+ stripped = dict(environment)
+ stripped.pop("HARBORRL_NATIVE_RUNNER_WORKERS")
+ with pytest.raises(ValueError, match="RUNNER_WORKERS"):
+ RolloutRuntimeConfig.from_environment(stripped, endpoint="x",
+ concurrency_limit=1, group_size=2)
+
+
+def test_runtime_binds_gateway_with_registry_and_backend(tmp_path, monkeypatch):
+ from harborrl.gateway import server as gateway_server
+ from harborrl.gateway import sglang_backend, trace
+ from harborrl.rollout import runtime as rollout_runtime
+
+ catalog = tmp_path / "catalog.json"
+ catalog.write_text("[]")
+ profile = tmp_path / "profile.json"
+ profile.write_text(json.dumps({"model": "policy"}))
+ tokenizer = SimpleNamespace(
+ apply_chat_template=lambda *args, **kwargs: [1, 2],
+ get_vocab=lambda: {"token": 1},
+ )
+ tokenizer_digest, template_digest = rollout_runtime.tokenizer_fingerprint(tokenizer)
+ environment = {
+ "RUN_DIR": str(tmp_path), "RUN_ID": "run-1",
+ "HARBORRL_NATIVE_CATALOG": str(catalog),
+ "HARBORRL_NATIVE_PROFILE": str(profile),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": tokenizer_digest,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": template_digest,
+ "HARBORRL_NATIVE_GATEWAY_HOST": "127.0.0.1",
+ "HARBORRL_NATIVE_GATEWAY_PORT": "49183",
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:49183",
+ "HARBORRL_NATIVE_RUNNER_PYTHON": "/runner/python",
+ "HARBORRL_NATIVE_RUNNER_WORKERS": '["worker-a"]',
+ "ROLLOUT_MAX_RESPONSE_LEN": "128",
+ "ROLLOUT_MAX_CONTEXT_LEN": "1024",
+ }
+ for key, value in environment.items():
+ monkeypatch.setenv(key, value)
+
+ calls = {}
+
+ class FakeGateway:
+ def __init__(self, registry, backend, *, model, max_output_tokens):
+ calls["gateway"] = (registry, backend, model, max_output_tokens)
+ calls["gateway_object"] = self
+
+ class FakeServer:
+ def serve_forever(self):
+ pass
+
+ def shutdown(self):
+ pass
+
+ def server_close(self):
+ pass
+
+ class FakeTraceRegistry:
+ def __init__(self, path):
+ calls["registry_path"] = path
+
+ class FakeBackend:
+ def __init__(self, *args, **kwargs):
+ calls["backend_args"] = (args, kwargs)
+
+ def weight_version(self):
+ return "7"
+
+ def fake_make_server(gateway, host, port):
+ calls["server"] = (gateway, host, port)
+ return FakeServer()
+
+ monkeypatch.setattr(gateway_server, "Gateway", FakeGateway)
+ monkeypatch.setattr(gateway_server, "make_server", fake_make_server)
+ monkeypatch.setattr(sglang_backend, "SGLangBackend", FakeBackend)
+ monkeypatch.setattr(trace, "TraceRegistry", FakeTraceRegistry)
+
+ config = rollout_runtime.RolloutRuntimeConfig.from_environment(
+ environment, endpoint="http://127.0.0.1:30000", concurrency_limit=4, group_size=2)
+ runtime_ = rollout_runtime.RolloutRuntime(config, tokenizer)
+ try:
+ assert calls["gateway"][2:] == ("policy", 128)
+ assert calls["server"][0] is calls["gateway_object"]
+ assert calls["server"][1:] == ("127.0.0.1", 49183)
+ assert calls["backend_args"][0][0] == "http://127.0.0.1:30000"
+ assert runtime_.backend.weight_version() == "7"
+ finally:
+ runtime_.close()
+
+ # A tokenizer that disagrees with the native lock fails closed.
+ other = SimpleNamespace(apply_chat_template=lambda *a, **k: [9],
+ get_vocab=lambda: {"other": 2})
+ with pytest.raises(ValueError, match="digest"):
+ rollout_runtime.RolloutRuntime(config, other)
+
+
+def test_policy_identity_uses_synchronized_pool_version(tmp_path, monkeypatch):
+ from harborrl.rollout import runtime as rollout_runtime
+
+ class Backend:
+ endpoint = "http://router:30000"
+
+ def pool_states(self):
+ return [{"endpoint": self.endpoint, "healthy": True, "weight_version": "2"}]
+
+ class Registry:
+ def __init__(self):
+ self.published = None
+
+ def drain(self, timeout):
+ self.timeout = timeout
+
+ def publish_version(self, states, expected):
+ self.published = (states, expected)
+
+ monkeypatch.setenv("RUN_DIR", str(tmp_path))
+ registry = Registry()
+ runtime_ = SimpleNamespace(run_root=tmp_path, backend=Backend(), registry=registry,
+ config=SimpleNamespace(drain_timeout=30))
+ assert rollout_runtime.current_policy_version(runtime_) == "2"
+ payload = json.loads((tmp_path / "policy-pool.json").read_text())
+ assert payload["weight_version"] == "2"
+ assert registry.published[1] == "2"
+ assert registry.timeout == 30
+
+
+def test_group_shares_slot_state_and_shifts_retry_workers(tmp_path, monkeypatch):
+ from harborrl.rollout import runtime as rollout_runtime
+
+ entry = {"id": "task", "revision": "1", "path": "/task", "task_digest": "d" * 64,
+ "reward_profile": {"key": "reward", "scale": 1.0, "offset": 0.0,
+ "raw_range": [0.0, 1.0]}}
+ profile = tmp_path / "profile.json"
+ profile.write_text(json.dumps({"model": "policy"}))
+ environment = {
+ "RUN_ID": "run", "RUN_DIR": str(tmp_path), "HARBORRL_NATIVE_PROFILE": str(profile),
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(["worker-a", "worker-b"]),
+ "HARBORRL_NATIVE_MAX_ATTEMPTS": "2",
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:32123",
+ "HARBORRL_NATIVE_GATEWAY_PORT": "32123",
+ "HARBORRL_NATIVE_RUNNER_PYTHON": "/python",
+ "HARBORRL_NATIVE_CATALOG": str(tmp_path / "catalog.json"),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": "0" * 64,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": "1" * 64,
+ }
+ for key, value in environment.items():
+ monkeypatch.setenv(key, value)
+ config = rollout_runtime.RolloutRuntimeConfig.from_environment(
+ environment, endpoint="http://router:30000", concurrency_limit=8, group_size=4)
+
+ class FakeSlots:
+ max_attempts = 2
+
+ def __init__(self):
+ self.started = {}
+
+ def start(self, slot):
+ self.started[slot] = self.started.get(slot, 0) + 1
+ return SimpleNamespace(attempt_id=f"{slot}-{self.started[slot]}",
+ group_id="group-0", slot_id=slot,
+ attempt_no=self.started[slot])
+
+ def finish(self, slot, ir=None, error=None):
+ assert (ir is None) != (error is None)
+
+ created_groups = []
+
+ def fake_group_slots(identities, *, max_attempts=2):
+ created_groups.append(identities)
+ return FakeSlots()
+
+ calls = []
+
+ async def fake_run_attempt(identity, *, runner_host, **kwargs):
+ calls.append((identity.slot_id, runner_host))
+ if identity.slot_id == "1" and identity.attempt_no == 1:
+ raise RuntimeError("simulated first-attempt failure")
+ return {"identity": identity.__dict__}
+
+ monkeypatch.setattr(rollout_runtime, "GroupSlots", fake_group_slots)
+ monkeypatch.setattr(rollout_runtime, "run_attempt", fake_run_attempt)
+
+ runtime_ = SimpleNamespace(rollout_root=tmp_path, semaphore=asyncio.Semaphore(8),
+ registry=SimpleNamespace(), profile={"model": "policy"})
+
+ # The run owns its loaded profile; group setup must not reread the source file.
+ profile.unlink()
+ results = asyncio.run(rollout_runtime.run_group(
+ config, runtime_, entry=entry, group_index=0, rollout_id=0,
+ sampling_digest="sampling", policy_version="1"))
+ assert len(results) == 4
+ assert len(created_groups) == 1
+ assert len(created_groups[0]) == 4
+ assert {item.harness_digest for item in created_groups[0]} == {
+ rollout_runtime.digest(runtime_.profile)}
+ by_slot = {}
+ for slot, host in calls:
+ by_slot.setdefault(slot, []).append(host)
+ assert by_slot == {"0": ["worker-a"], "1": ["worker-b", "worker-a"],
+ "2": ["worker-a"], "3": ["worker-b"]}
+
+
+def test_task_worker_override_wins(tmp_path, monkeypatch):
+ from harborrl.rollout import runtime as rollout_runtime
+
+ entry = {"id": "task", "revision": "1", "path": "/task", "task_digest": "d" * 64,
+ "reward_profile": {"key": "reward"}}
+ profile = tmp_path / "profile.json"
+ profile.write_text(json.dumps({"model": "policy"}))
+ environment = {
+ "RUN_ID": "run", "RUN_DIR": str(tmp_path), "HARBORRL_NATIVE_PROFILE": str(profile),
+ "HARBORRL_NATIVE_RUNNER_WORKERS": json.dumps(["worker-a", "worker-b"]),
+ "HARBORRL_NATIVE_TASK_WORKER_OVERRIDES": json.dumps({"task": "worker-b"}),
+ "HARBORRL_NATIVE_GATEWAY_URL": "http://gateway:32123",
+ "HARBORRL_NATIVE_GATEWAY_PORT": "32123", "HARBORRL_NATIVE_RUNNER_PYTHON": "/python",
+ "HARBORRL_NATIVE_CATALOG": str(tmp_path / "catalog.json"),
+ "HARBORRL_NATIVE_TOKENIZER_DIGEST": "0" * 64,
+ "HARBORRL_NATIVE_TEMPLATE_DIGEST": "1" * 64,
+ }
+ for key, value in environment.items():
+ monkeypatch.setenv(key, value)
+ config = rollout_runtime.RolloutRuntimeConfig.from_environment(
+ environment, endpoint="http://router:30000", concurrency_limit=2, group_size=2)
+
+ seen = []
+
+ class PermissiveSlots:
+ max_attempts = 2
+
+ def __init__(self, identities, *, max_attempts=2):
+ self.identities = {identity.slot_id: identity for identity in identities}
+
+ def start(self, slot):
+ return self.identities[slot]
+
+ def finish(self, slot, ir=None, error=None):
+ assert (ir is None) != (error is None)
+
+ async def fake_run_attempt(identity, *, runner_host, **kwargs):
+ seen.append(runner_host)
+ return {"identity": identity.to_dict()}
+
+ monkeypatch.setattr(rollout_runtime, "GroupSlots", PermissiveSlots)
+ monkeypatch.setattr(rollout_runtime, "run_attempt", fake_run_attempt)
+ runtime_ = SimpleNamespace(rollout_root=tmp_path, semaphore=asyncio.Semaphore(2),
+ registry=SimpleNamespace(), profile={"model": "policy"})
+ irs = asyncio.run(rollout_runtime.run_group(
+ config, runtime_, entry=entry, group_index=0, rollout_id=0,
+ sampling_digest="sampling", policy_version="1"))
+ assert len(irs) == 2
+ assert seen == ["worker-b", "worker-b"]
+
+
+def test_rollout_core_does_not_import_training_frameworks():
+ import re
+
+ import harborrl.rollout
+
+ root = Path(harborrl.rollout.__file__).parent
+ offenders = []
+ for path in sorted(root.rglob("*.py")):
+ for number, line in enumerate(path.read_text().splitlines(), 1):
+ if re.match(r"\s*(from|import)\s+(slime|ray)(\.|\s|$)", line):
+ offenders.append(f"{path.relative_to(root)}:{number}: {line.strip()}")
+ assert not offenders, "rollout core must stay framework-free: " + "; ".join(offenders)
+
+
+def test_native_generate_shim_reexports_the_core():
+ from harborrl import rollout as package
+ from harborrl.rollout import native_generate, runtime
+
+ for name in ("NativeRolloutRuntime", "RolloutRuntime", "RolloutRuntimeConfig",
+ "close_runtime", "current_policy_version", "publish_native_version",
+ "run_attempt", "run_group", "run_slot", "runtime", "task_entry",
+ "tokenizer_fingerprint", "GroupSlots"):
+ assert getattr(native_generate, name) is getattr(runtime, name), name
+ assert package.native_generate.__doc__ is not None
diff --git a/tests/harborrl/test_rollout_service.py b/tests/harborrl/test_rollout_service.py
new file mode 100644
index 000000000..f12994feb
--- /dev/null
+++ b/tests/harborrl/test_rollout_service.py
@@ -0,0 +1,415 @@
+"""RolloutService + SlimeAdapter contracts (P3) over real local HTTP."""
+from __future__ import annotations
+
+import asyncio
+import json
+import threading
+import time
+from pathlib import Path
+from types import SimpleNamespace
+
+import pytest
+from test_native_contracts import SAMPLING, receipt
+
+from harborrl.backends.slime_v032.rollout_service_client import (
+ RolloutServiceClient,
+ ServiceRolloutError,
+ load_group_result,
+ service_group_request,
+)
+from harborrl.rollout.service import (
+ RolloutService,
+ make_control_server,
+ manifest_path_from,
+)
+from harborrl.trajectories.native import Identity, RewardProfile, digest, publish
+
+TASK = {"id": "task", "revision": "1", "path": "/task", "task_digest": "d" * 64,
+ "reward_profile": {"key": "reward", "scale": 1.0, "offset": 0.0,
+ "raw_range": [0.0, 1.0]}}
+
+
+def service_identity(slot, *, run_id="run-1", group_id="group-0", policy_version="1"):
+ return Identity(run_id=run_id, batch_id="rollout-0", group_id=group_id,
+ slot_id=slot, attempt_id=f"attempt-{policy_version}-{slot}",
+ trajectory_id=f"trajectory-{policy_version}-{slot}",
+ task_digest=TASK["task_digest"], policy_version=policy_version,
+ harness_digest="claude", reward_digest=RewardProfile().digest,
+ sampling_digest=digest(SAMPLING))
+
+
+def service_ir(tmp_path, slot, reward=1, **identity_overrides):
+ from harborrl.trajectories.native import assemble
+
+ ident = service_identity(slot, **identity_overrides)
+ evaluation = receipt(tmp_path / slot, ident, reward)
+ trial = evaluation["harbor_trial_id"]
+ turn = {"identity": ident.to_dict(), "harbor_trial_id": trial,
+ "request_id": f"r-{slot}", "response_id": f"m-{slot}", "engine_id": "engine",
+ "input_ids": [1, 2], "output_ids": [3, 4], "logprobs": [-0.2, -0.3],
+ "logprob_semantics": "raw_model", "policy_version": ident.policy_version,
+ "tokenizer_digest": "tok", "template_digest": "template",
+ "delivery": "consumed_confirmed", "consumption_ref": "session#digest",
+ "role": "policy", "sampling": SAMPLING, "client_request": {"model": "policy"},
+ "serving_input": {"input_ids": [1, 2]},
+ "response": {"content": [{"type": "text", "text": "done"}]},
+ "finish_reason": "end_turn", "evidence_kind": "synthetic"}
+ for key in ("client_request", "serving_input", "response"):
+ turn[key + "_digest"] = digest(turn[key])
+ seal = {"identity": ident.to_dict(), "harbor_trial_id": trial,
+ "pending_requests": 0, "status": "sealed",
+ "request_ids": [turn["request_id"]], "turns_digest": digest([turn]),
+ "errors": []}
+ return assemble(ident, trial, [turn], seal, evaluation)
+
+
+class FakeRegistry:
+ def __init__(self):
+ self.version = None
+ self.accepting = False
+
+ def drain(self, timeout):
+ self.accepting = False
+
+ def publish_version(self, states, expected):
+ self.version = str(expected)
+ self.accepting = True
+
+
+class FakeRuntime:
+ def __init__(self, root):
+ self.registry = FakeRegistry()
+ self.rollout_root = Path(root)
+
+ def manifest_path(self, identity):
+ return manifest_path_from(self.rollout_root, identity)
+
+ def close(self):
+ pass
+
+
+def write_group_irs(tmp_path, request, rewards=(1, 0)):
+ runtime = FakeRuntime(tmp_path)
+ irs = []
+ for descriptor, reward in zip(request["slots"], rewards):
+ ir = service_ir(tmp_path, descriptor["slot_id"], reward,
+ policy_version=request["policy_version"])
+ publish(runtime.manifest_path(ir["identity"]), ir)
+ irs.append(ir)
+ return irs
+
+
+def group_request(group_index=0, policy_version="1", slots=2, task=TASK):
+ return {
+ "group_id": f"group-{group_index}", "rollout_id": 0,
+ "group_index": group_index, "policy_version": policy_version,
+ "task": task, "sampling": dict(SAMPLING),
+ "slots": [{"slot_id": str(slot)} for slot in range(slots)],
+ }
+
+
+class ServiceHarness:
+ def __init__(self, tmp_path, executor, **kwargs):
+ self.tmp_path = tmp_path
+ self.runtime = FakeRuntime(tmp_path)
+ self.service = RolloutService(run_root=tmp_path, executor=executor, allow_synthetic=True, **kwargs)
+ self.control = make_control_server(self.service, host="127.0.0.1", port=0)
+ self.thread = threading.Thread(target=self.control.serve_forever, daemon=True)
+ self.thread.start()
+ self.url = f"http://127.0.0.1:{self.control.server_address[1]}"
+ self.run_id = "run-1"
+ registration = self.service.register_run(
+ self.run_id, {"run_id": self.run_id, "group_size": 2})
+ self.token = registration["run_token"]
+ self.service.attach_runtime(self.run_id, SimpleNamespace(drain_timeout=1),
+ self.runtime)
+
+ def client(self):
+ return RolloutServiceClient(self.url, self.run_id, self.token, timeout=10,
+ poll_interval=0.05)
+
+ def close(self):
+ self.control.shutdown()
+ self.control.server_close()
+ self.thread.join(5)
+ self.service.close()
+
+
+@pytest.fixture()
+def harness(tmp_path):
+ calls = []
+
+ async def executor(config, runtime_, request):
+ calls.append(json.loads(json.dumps(request)))
+ return write_group_irs(tmp_path, request)
+
+ instance = ServiceHarness(tmp_path, executor, group_wait_timeout=5)
+ instance.executor_calls = calls
+ try:
+ yield instance
+ finally:
+ instance.close()
+
+
+def activated_states(version="1"):
+ return [{"endpoint": f"http://engine-{index}", "healthy": True,
+ "weight_version": version} for index in range(2)]
+
+
+def test_register_auth_and_idempotency(harness):
+ client = harness.client()
+ assert client.health()["ok"]
+ # Wrong tokens are rejected on every run-scoped endpoint.
+ bad = RolloutServiceClient(harness.url, harness.run_id, "wrong-token", timeout=10)
+ with pytest.raises(ServiceRolloutError) as exc:
+ bad.group_status("group-0")
+ assert exc.value.status == 401
+ with pytest.raises(ServiceRolloutError) as exc:
+ bad.submit_group(group_request())
+ assert exc.value.status == 401
+
+
+def test_policy_gate_and_activation(harness, tmp_path):
+ client = harness.client()
+ with pytest.raises(ServiceRolloutError) as exc:
+ client.submit_group(group_request())
+ assert exc.value.status == 409 and exc.value.code == "stale_policy"
+ assert not (tmp_path / "policy-pool.json").exists()
+
+ client.activate_policy(activated_states(), "1")
+ assert harness.runtime.registry.version == "1"
+ assert harness.runtime.registry.accepting
+ pool = json.loads((tmp_path / "policy-pool.json").read_text())
+ assert pool["weight_version"] == "1"
+
+ # A partially unhealthy pool is never admitted.
+ from harborrl.rollout.service import ServiceError
+
+ broken = activated_states()
+ broken[1]["healthy"] = False
+ with pytest.raises(ServiceError, match="pool"):
+ harness.service.activate_policy(harness.run_id, broken, "2")
+
+
+def test_submit_is_idempotent_and_conflicts_rejected(harness):
+ client = harness.client()
+ client.activate_policy(activated_states(), "1")
+ request = group_request()
+ first = client.submit_group(request)
+ assert first["status"] == "complete"
+ assert len(first["result"]["slots"]) == 2
+ assert len(harness.executor_calls) == 1
+
+ again = client.submit_group(json.loads(json.dumps(request)))
+ assert again["status"] == "complete"
+ assert again["result"]["request_digest"] == first["result"]["request_digest"]
+ assert len(harness.executor_calls) == 1 # no second execution
+
+ conflicting = group_request()
+ conflicting["sampling"]["temperature"] = 0.5
+ with pytest.raises(ServiceRolloutError) as exc:
+ client.submit_group(conflicting)
+ assert exc.value.status == 409 and exc.value.code == "conflict"
+
+ other = client.submit_group(group_request(group_index=1))
+ assert other["status"] == "complete"
+ assert len(harness.executor_calls) == 2
+
+
+def test_client_validates_result_and_rejects_tampering(harness, tmp_path):
+ client = harness.client()
+ client.activate_policy(activated_states(), "1")
+ request = group_request()
+ result = client.submit_group(request)["result"]
+ trajectories = load_group_result(harness.run_id, request, result,
+ allow_synthetic=True)
+ assert [ir["identity"]["slot_id"] for ir in trajectories] == ["0", "1"]
+
+ slot = result["slots"][0]
+ manifest = Path(slot["manifest_path"])
+ forged = json.loads(json.dumps(result))
+ forged["slots"][0]["manifest_sha256"] = "0" * 64
+ with pytest.raises(ServiceRolloutError, match="digest mismatch"):
+ load_group_result(harness.run_id, request, forged, allow_synthetic=True)
+ tampered = json.loads(json.dumps(result))
+ tampered["slots"][1]["manifest_path"] = str(tmp_path / "missing.json")
+ with pytest.raises(ServiceRolloutError, match="cannot read slot manifest"):
+ load_group_result(harness.run_id, request, tampered, allow_synthetic=True)
+ assert manifest.is_file() # nothing above mutated shared evidence
+
+ # A stale policy version inside a result is rejected.
+ fresh = write_group_irs(tmp_path, group_request(policy_version="2"), rewards=(1, 0))
+ stale_result = json.loads(json.dumps(result))
+ stale_result["slots"][0]["manifest_path"] = str(
+ harness.runtime.manifest_path(fresh[0]["identity"]))
+ stale_result["slots"][0]["manifest_sha256"] = digest(fresh[0])
+ with pytest.raises(ServiceRolloutError, match="identity differs"):
+ load_group_result(harness.run_id, request, stale_result, allow_synthetic=True)
+
+
+def test_drain_closes_admission(harness):
+ client = harness.client()
+ client.activate_policy(activated_states(), "1")
+ client.drain()
+ with pytest.raises(ServiceRolloutError) as exc:
+ client.submit_group(group_request())
+ assert exc.value.status == 409 and exc.value.code == "admission_closed"
+ assert not harness.runtime.registry.accepting
+
+
+@pytest.mark.parametrize("cancel_whole_run", [False, True])
+def test_cancel_and_query_after_timeout(tmp_path, cancel_whole_run):
+ release = threading.Event()
+
+ async def slow_executor(config, runtime_, request):
+ if request["group_index"] == 0:
+ while not release.is_set():
+ await asyncio.sleep(0.05) # cancellable wait
+ return write_group_irs(tmp_path, request)
+ while True: # the second group blocks until it is cancelled
+ await asyncio.sleep(0.05)
+
+ harness = ServiceHarness(tmp_path, slow_executor, group_wait_timeout=0.2)
+ try:
+ client = harness.client()
+ client.activate_policy(activated_states(), "1")
+ request = group_request()
+ # Short server wait: the response must say running and hint a query.
+ pending = harness.service.submit_group(harness.run_id, request)
+ assert pending["status"] in ("queued", "running")
+ assert "hint" in pending
+ status = client.group_status("group-0")
+ assert status["status"] in ("queued", "running")
+ release.set()
+ deadline = time.time() + 5
+ while time.time() < deadline and status["status"] in ("queued", "running"):
+ time.sleep(0.05)
+ status = client.group_status("group-0")
+ assert status["status"] == "complete"
+
+ request2 = group_request(group_index=1)
+ submitted = harness.service.submit_group(harness.run_id, request2)
+ assert submitted["status"] in ("queued", "running")
+ if cancel_whole_run:
+ result = harness.service.cancel_run(harness.run_id)
+ assert result["groups"] == {"group-0": "complete", "group-1": "cancelled"}
+ cancelled = client.group_status("group-1")
+ else:
+ cancelled = client.cancel_group("group-1")
+ assert cancelled["status"] == "cancelled"
+ with pytest.raises(ServiceRolloutError, match="cancelled|not complete"):
+ load_group_result(harness.run_id, request2, cancelled)
+ finally:
+ release.set()
+ harness.close()
+
+
+def test_register_run_conflict_on_different_manifest(tmp_path):
+ async def executor(config, runtime_, request):
+ return write_group_irs(tmp_path, request)
+
+ harness = ServiceHarness(tmp_path, executor)
+ try:
+ from harborrl.rollout.service import ServiceError
+
+ same_manifest = {"run_id": "run-1", "group_size": 2}
+ assert harness.service.register_run("run-1", same_manifest)["existed"]
+ with pytest.raises(ServiceError) as exc:
+ harness.service.register_run("run-1", {"run_id": "run-1", "x": 1})
+ assert exc.value.status == 409
+ finally:
+ harness.close()
+
+
+def test_service_group_request_and_run_service_groups(tmp_path, monkeypatch):
+ """Full SlimeAdapter path: fake Slime samples -> HTTP -> IRs bound to samples."""
+ from harborrl.backends.slime_v032 import rollout as slime_rollout
+
+ calls = []
+
+ async def executor(config, runtime_, request):
+ calls.append(request["group_id"])
+ return write_group_irs(tmp_path, request)
+
+ harness = ServiceHarness(tmp_path, executor)
+ try:
+ monkeypatch.setenv("HARBORRL_ROLLOUT_SERVICE_URL", harness.url)
+ monkeypatch.setenv("HARBORRL_ROLLOUT_SERVICE_TOKEN", harness.token)
+ monkeypatch.setenv("RUN_ID", harness.run_id)
+ monkeypatch.setenv("RUN_DIR", str(tmp_path))
+
+ class Sample:
+ class Status:
+ COMPLETED = "completed"
+
+ def __init__(self, group_index):
+ self.metadata = {"native_task": dict(TASK)}
+ self.group_index = group_index
+
+ args = SimpleNamespace(rollout_max_response_len=SAMPLING["max_new_tokens"],
+ rollout_temperature=1.0, rollout_top_p=1.0,
+ rollout_top_k=-1, rollout_stop=[],
+ sglang_router_ip="127.0.0.1", sglang_router_port=30000)
+ group = [Sample(0), Sample(0)]
+ monkeypatch.setenv("HARBORRL_ROLLOUT_SERVICE_TOKEN", harness.token)
+ # The adapter announces serving and derives the admitted version from
+ # the live pool itself before requesting groups.
+ from harborrl.backends.slime_v032 import rollout_service_client
+
+ monkeypatch.setattr(rollout_service_client, "fetch_pool_states",
+ lambda endpoint, **kwargs: activated_states())
+
+ with pytest.raises(ServiceRolloutError, match='synthetic_evidence'):
+ asyncio.run(slime_rollout.run_service_groups(args, [group], 0))
+ assert all('native_ir' not in sample.metadata for sample in group)
+ assert calls == ["group-0"]
+ assert harness.service.runs[harness.run_id].serving == (
+ "http://127.0.0.1:30000")
+
+ request = service_group_request(
+ group, slime_rollout.sampling_params(args), 0, "1")
+ assert request["group_id"] == "group-0"
+ assert request["group_index"] == 0
+ assert [slot["slot_id"] for slot in request["slots"]] == ["0", "1"]
+ finally:
+ harness.close()
+
+
+def test_announce_factory_attaches_config_and_runtime(tmp_path):
+ """A GPU announce through the factory makes the run executable alone."""
+ async def executor(config, runtime_, request):
+ assert config is runtime_.config
+ return write_group_irs(tmp_path, request)
+
+ service = RolloutService(run_root=tmp_path, executor=executor, allow_synthetic=True,
+ group_wait_timeout=5,
+ runtime_factory=lambda run_id, router: SimpleNamespace(
+ registry=FakeRegistry(), rollout_root=tmp_path,
+ manifest_path=lambda ident: manifest_path_from(tmp_path, ident),
+ config=SimpleNamespace(drain_timeout=1)))
+ try:
+ registration = service.register_run("run-9", {"run_id": "run-9"})
+ result = service.announce_serving("run-9", "http://gpu:30000/")
+ assert result["runtime"] == "attached"
+ assert service.runs["run-9"].serving == "http://gpu:30000"
+ assert service.runs["run-9"].config is not None
+ service.activate_policy("run-9", activated_states(), "1")
+ submitted = service.submit_group("run-9", group_request())
+ assert submitted["status"] == "complete"
+ assert registration["run_token"]
+ finally:
+ service.close()
+
+
+def test_service_group_request_rejects_mixed_groups():
+ class Sample:
+ def __init__(self, group_index, task):
+ self.metadata = {"native_task": dict(task)}
+ self.group_index = group_index
+
+ other = dict(TASK)
+ other["revision"] = "2"
+ with pytest.raises(ValueError, match="mixes task locks"):
+ service_group_request([Sample(0, TASK), Sample(0, other)], dict(SAMPLING), 0, "1")
+ with pytest.raises(ValueError, match="group identity"):
+ service_group_request([Sample(0, TASK), Sample(1, TASK)], dict(SAMPLING), 0, "1")
diff --git a/tests/harborrl/test_sandbox.py b/tests/harborrl/test_sandbox.py
new file mode 100644
index 000000000..b98d27197
--- /dev/null
+++ b/tests/harborrl/test_sandbox.py
@@ -0,0 +1,97 @@
+"""Sandbox routing and fail-closed capacity contracts, without live resources."""
+import asyncio
+import json
+from pathlib import Path
+
+import pytest
+import yaml
+from test_native_training import native_config
+
+from harborrl.config.native import load_config
+from harborrl.rollout.harbor_job.sandbox_config import (
+ SandboxAdmission,
+ load_sandbox,
+ task_binding,
+)
+
+
+def settings(tmp_path):
+ return {'domain': 'https://sandbox.example', 'credential_file': 'key',
+ 'bindings': [{'image': 'registry.example/task@sha256:' + 'a' * 64,
+ 'max_live_instances': 1}],
+ 'installation': {'mode': 'offline_binary', 'version': '2.1.0', 'binary': 'claude', 'sha256': 'b' * 64}}
+
+
+def test_local_runner_opt_in_and_unknown_fields(tmp_path):
+ path, _ = native_config(tmp_path)
+ old = load_config(path)
+ assert old['harbor']['workers'] == ['worker-a', 'worker-b']
+ local = load_config(path, ['harbor.runner_transport=local', 'harbor.workers=[]',
+ 'harbor.environment={runtime: sandbox, config: sandbox.yaml}'])
+ assert local['harbor']['environment']['config'] == str(tmp_path / 'sandbox.yaml')
+ with pytest.raises(ValueError, match='local runner'):
+ load_config(path, ['harbor.runner_transport=local'])
+ with pytest.raises(ValueError, match='fields'):
+ load_config(path, ['harbor.environment={runtime: sandbox, config: x, api_key: secret}'])
+
+
+def test_sandbox_binding_and_secret_rejection(tmp_path):
+ cfg = settings(tmp_path)
+ path = tmp_path / 'sandbox.yaml'
+ path.write_text(yaml.safe_dump(cfg))
+ result = load_sandbox(path)
+ assert result['credential_file'] == str(tmp_path / 'key')
+ (tmp_path / 'task.toml').write_text('[environment]\ndocker_image = "' + cfg['bindings'][0]['image'] + '"\n')
+ assert task_binding(result, tmp_path)['image'] == cfg['bindings'][0]['image']
+ (tmp_path / 'task.toml').write_text('[environment]\ndocker_image = "wrong"\n')
+ with pytest.raises(ValueError, match='exact binding'):
+ task_binding(result, tmp_path)
+ cfg['api_key'] = 'must-not-be-serialized'
+ path.write_text(yaml.safe_dump(cfg))
+ with pytest.raises(ValueError, match='fields'):
+ load_sandbox(path)
+
+
+def test_admission_across_groups_and_cleanup_debt(tmp_path):
+ async def run():
+ cfg = {'max_live_instances': 2, 'bindings': [{'image': 'image', 'max_live_instances': 1}]}
+ admission = SandboxAdmission(cfg)
+ active, peak = 0, 0
+
+ async def trial(index):
+ nonlocal active, peak
+ async with admission.lease(cfg['bindings'][0], tmp_path / str(index)):
+ active += 1
+ peak = max(peak, active)
+ await asyncio.sleep(0.01)
+ active -= 1
+ await asyncio.gather(*(trial(i) for i in range(6)))
+ assert peak == 1
+ root = tmp_path / 'failed'
+ root.mkdir()
+ async with admission.lease(cfg['bindings'][0], root):
+ (root / 'cleanup-required.json').write_text('{}')
+ with pytest.raises(RuntimeError, match='blocked'):
+ async with admission.lease(cfg['bindings'][0], tmp_path):
+ pytest.fail('must not allocate')
+ asyncio.run(run())
+
+
+def test_sandbox_launch_freezes_nonsecret_runtime(tmp_path):
+ from harborrl.backends.slime_v032.config import launch_plan
+ from harborrl.data.harbor.native_inspector import inspect_native
+ path, task = native_config(tmp_path)
+ cfg = settings(tmp_path)
+ (tmp_path / 'sandbox.yaml').write_text(yaml.safe_dump(cfg))
+ (task / 'task.toml').write_text('[environment]\ndocker_image = "' + cfg['bindings'][0]['image'] + '"\n')
+ catalog = tmp_path / 'catalog.json'
+ entries = json.loads(catalog.read_text())
+ entries[0]['task_digest'] = inspect_native(task)['task_digest']
+ catalog.write_text(json.dumps(entries))
+ config = load_config(path, ['harbor.runner_transport=local', 'harbor.workers=[]',
+ 'harbor.environment={runtime: sandbox, config: sandbox.yaml}'])
+ plan = launch_plan(config)
+ runtime = json.loads(plan['environment']['HARBORRL_NATIVE_SANDBOX'])
+ assert runtime['installation']['version'] == '2.1.0'
+ assert Path(runtime['installation']['binary']).is_absolute()
+ assert plan['environment']['HARBORRL_NATIVE_RUNNER_WORKERS'] == '[]'
diff --git a/tests/harborrl/test_sandbox_allocation_transport.py b/tests/harborrl/test_sandbox_allocation_transport.py
new file mode 100644
index 000000000..db34cf098
--- /dev/null
+++ b/tests/harborrl/test_sandbox_allocation_transport.py
@@ -0,0 +1,24 @@
+import asyncio
+
+import httpx
+
+from harborrl.rollout.harbor_job.sandbox_transport import SandboxStartupTransport
+
+
+def test_generic_transport_observes_allocation_without_retry():
+ async def run():
+ calls, allocations = [], []
+
+ def handler(request):
+ calls.append(request.method)
+ return httpx.Response(200, json={'id': 'sbx-one'}) if request.method == 'POST' else httpx.Response(503)
+
+ transport = SandboxStartupTransport('https://sandbox.example/v1', allocations.append,
+ transport=httpx.MockTransport(handler))
+ async with httpx.AsyncClient(transport=transport) as client:
+ await client.post('https://sandbox.example/v1/sandboxes')
+ result = await client.get('https://sandbox.example/v1/sandboxes/sbx-one/endpoints/44772')
+ assert result.status_code == 503
+ assert calls == ['POST', 'GET'] and allocations == ['sbx-one']
+
+ asyncio.run(run())
diff --git a/tests/harborrl/test_sandbox_cleanup.py b/tests/harborrl/test_sandbox_cleanup.py
new file mode 100644
index 000000000..7364ac483
--- /dev/null
+++ b/tests/harborrl/test_sandbox_cleanup.py
@@ -0,0 +1,36 @@
+import asyncio
+
+import httpx
+import pytest
+
+pytest.importorskip('harbor.environments.opensandbox')
+
+from harborrl.rollout.harbor_job.environments.opensandbox import (
+ ManagedOpenSandboxEnvironment,
+)
+
+
+@pytest.mark.parametrize('delete_status,expected', [(204, 'deleted'), (500, 'delete_failed')])
+def test_allocated_id_cleanup_requires_confirmed_absence(tmp_path, monkeypatch, delete_status, expected):
+ calls = []
+
+ async def handler(request):
+ calls.append((request.method, request.url.path))
+ assert request.url.path == '/v1/sandboxes/sbx-owned'
+ if request.method == 'DELETE':
+ return httpx.Response(delete_status)
+ return httpx.Response(200 if len(calls) == 1 else 404)
+
+ client_class = httpx.AsyncClient
+ monkeypatch.setattr(httpx, 'AsyncClient', lambda **kwargs: client_class(
+ **kwargs, transport=httpx.MockTransport(handler)))
+ env = object.__new__(ManagedOpenSandboxEnvironment)
+ env.cfg = {'domain': 'https://platform.test', 'cleanup_timeout_s': 1}
+ env.binding = {'image': 'owned'}
+ env._api_key = 'test-only'
+ env.evidence = tmp_path
+ env.record = {'sandbox_id': 'sbx-owned', 'runtime_digest': 'test', 'events': []}
+ asyncio.run(env._cleanup_allocated())
+ assert env.record['state'] == expected
+ assert (tmp_path / 'cleanup-required.json').exists() == (expected == 'delete_failed')
+ assert calls[:2] == [('GET', '/v1/sandboxes/sbx-owned'), ('DELETE', '/v1/sandboxes/sbx-owned')]
diff --git a/tests/harborrl/test_sandbox_portability.py b/tests/harborrl/test_sandbox_portability.py
new file mode 100644
index 000000000..1a4c5bb6a
--- /dev/null
+++ b/tests/harborrl/test_sandbox_portability.py
@@ -0,0 +1,57 @@
+"""Public core must remain usable without the optional SDK or cluster package."""
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+import yaml
+
+from harborrl.rollout.harbor_job.sandbox_config import load_sandbox, read_credential
+
+
+def test_config_loading_does_not_import_optional_packages(tmp_path):
+ config = tmp_path / 'sandbox.yaml'
+ config.write_text(yaml.safe_dump({'domain': 'https://sandbox.example', 'credential_file': 'key',
+ 'bindings': [{'image': 'image', 'max_live_instances': 1}]}))
+ code = """
+import sys
+from harborrl.rollout.harbor_job.sandbox_config import load_sandbox
+cfg = load_sandbox(sys.argv[1])
+assert cfg['installation'] is None and cfg['options'] == {}
+assert not any(k == 'harbor' or k.startswith(('harbor.', 'opensandbox', 'harborrl_h_cluster', 'torch', 'ray')) for k in sys.modules)
+"""
+ subprocess.run([sys.executable, '-c', code, str(config)], check=True)
+
+
+def test_core_has_no_cluster_imports_or_platform_keys():
+ root = Path(__file__).resolve().parents[2] / 'harborrl'
+ for path in root.rglob('*.py'):
+ source = path.read_text()
+ assert 'harborrl_h_cluster' not in source, str(path)
+ assert 'brainbox' not in source.lower(), str(path)
+ assert 'environment_id' not in source, str(path)
+
+
+@pytest.mark.parametrize('adapter', ['invalid', '../module:Class', 'module:Class:Other'])
+def test_adapter_path_rejected_without_import(tmp_path, adapter):
+ path = tmp_path / 'sandbox.yaml'
+ path.write_text(yaml.safe_dump({'domain': 'https://sandbox.example', 'credential_file': 'key',
+ 'bindings': [{'image': 'image', 'max_live_instances': 1}], 'adapter': adapter}))
+ with pytest.raises(ValueError, match='module:Class'):
+ load_sandbox(path)
+
+
+def test_generic_token_does_not_require_platform_key_format(tmp_path):
+ path = tmp_path / 'credential'
+ path.write_text('test-only\n')
+ path.chmod(0o600)
+ assert read_credential({'credential_file': str(path)}) == 'test-only'
+
+
+def test_nested_provider_secrets_never_enter_runtime_manifest(tmp_path):
+ path = tmp_path / 'sandbox.yaml'
+ path.write_text(yaml.safe_dump({'domain': 'https://sandbox.example', 'credential_file': 'key',
+ 'bindings': [{'image': 'image', 'max_live_instances': 1}],
+ 'options': {'nested': {'api_key': 'test-only'}}}))
+ with pytest.raises(ValueError, match='secrets'):
+ load_sandbox(path)
diff --git a/tests/harborrl/test_sandbox_runtime.py b/tests/harborrl/test_sandbox_runtime.py
new file mode 100644
index 000000000..9f896818f
--- /dev/null
+++ b/tests/harborrl/test_sandbox_runtime.py
@@ -0,0 +1,92 @@
+"""Shared Sandbox scheduling and service lifecycle contracts."""
+import asyncio
+import json
+from dataclasses import replace
+from types import SimpleNamespace
+
+import pytest
+from test_deployment_config import deployment_mapping
+
+from harborrl.backends.slime_v032.rollout_service_client import (
+ RolloutServiceClient,
+ ServiceRolloutError,
+)
+from harborrl.deployment.schema import validate
+from harborrl.rollout.harbor_job.sandbox_config import SandboxAdmission
+from harborrl.rollout.runtime import RolloutRuntimeConfig, run_slot
+
+
+def test_sandbox_deployment_allows_local_or_remote_runners(tmp_path):
+ cfg = deployment_mapping(tmp_path)
+ cfg['rollout'] = {'provider': 'sandbox', 'workers': []}
+ assert validate(cfg)['rollout']['workers'] == []
+ cfg['rollout']['workers'] = deployment_mapping(tmp_path)['rollout']['workers']
+ assert validate(cfg)['rollout']['workers']
+
+
+def test_slow_group_is_posted_once_even_after_transport_timeout(monkeypatch):
+ client = RolloutServiceClient('http://manager', 'run', 'token', poll_interval=0)
+ calls = []
+ def request(method, path, body=None):
+ calls.append(method)
+ if method == 'POST':
+ raise ServiceRolloutError('timeout', code='transport')
+ return {'status': 'running' if len(calls) == 2 else 'complete', 'result': {}}
+ monkeypatch.setattr(client, '_request', request)
+ assert client.submit_group({'group_id': 'group-0'})['status'] == 'complete'
+ assert calls == ['POST', 'GET', 'GET']
+
+
+def test_sandbox_cleanup_debt_prevents_retry(tmp_path, monkeypatch):
+ from harborrl.rollout import runtime
+ from harborrl.rollout.harbor_job import sandbox_config
+ profile = tmp_path / 'profile.json'
+ profile.write_text('{}')
+ binding = {'image': 'image', 'max_live_instances': 1}
+ sandbox = {'max_live_instances': 1, 'bindings': [binding]}
+ config = RolloutRuntimeConfig('run', str(tmp_path), '', str(profile), '127.0.0.1', 1,
+ 'http://gateway', '/python', (), {}, 2, 128, 1024, 2, 2, 'tok', 'tpl', True, 1,
+ 'qwen3-8B', 'http://router', sandbox=sandbox, runner_transport='local')
+ current = SimpleNamespace(rollout_root=tmp_path, semaphore=asyncio.Semaphore(2),
+ registry=None, profile={}, sandbox_admission=SandboxAdmission(sandbox))
+ calls = []
+ async def attempt(identity, **kwargs):
+ calls.append(kwargs)
+ assert kwargs['runner_host'] is None
+ assert kwargs['sandbox'] == sandbox
+ kwargs['root'].mkdir(parents=True)
+ (kwargs['root'] / 'cleanup-required.json').write_text('{}')
+ raise RuntimeError('deletion unconfirmed')
+ monkeypatch.setattr(runtime, 'run_attempt', attempt)
+ monkeypatch.setattr(sandbox_config, 'task_binding', lambda *args: binding)
+ entry = {'id': 'task', 'path': '/task', 'task_digest': 'digest', 'reward_profile': {}}
+ with pytest.raises(RuntimeError, match='cleanup unresolved'):
+ asyncio.run(run_slot(config, current, 0, 0, 0, entry, 'sampling', '1'))
+ assert len(calls) == 1 and current.sandbox_admission.blocked
+ restored = replace(config, workers=('worker',), runner_transport='ssh')
+ assert json.loads(restored.environment()['HARBORRL_NATIVE_SANDBOX']) == sandbox
+
+
+def test_group_failure_waits_for_sibling_cleanup(tmp_path, monkeypatch):
+ from harborrl.rollout import runtime
+ events = []
+ async def attempt(identity, **kwargs):
+ if identity.slot_id == '0':
+ await asyncio.sleep(.01)
+ raise RuntimeError('failed slot')
+ try:
+ await asyncio.sleep(60)
+ finally:
+ await asyncio.sleep(.01)
+ events.append('cleanup complete')
+ monkeypatch.setattr(runtime, 'run_attempt', attempt)
+ config = SimpleNamespace(run_id='run', group_size=2, max_attempts=1, workers=(),
+ task_worker_overrides={}, runner_transport='local', sandbox=None,
+ gateway_url='http://gateway', runner_python='/python', max_output_tokens=128)
+ current = SimpleNamespace(profile={}, rollout_root=tmp_path,
+ semaphore=asyncio.Semaphore(2), registry=None)
+ entry = {'id': 'task', 'path': '/task', 'task_digest': 'digest', 'reward_profile': {}}
+ with pytest.raises(RuntimeError, match='exhausted its retry budget'):
+ asyncio.run(runtime.run_group(config, current, entry=entry,
+ group_index=0, rollout_id=0, sampling_digest='x', policy_version='1'))
+ assert events == ['cleanup complete']
diff --git a/tests/harborrl/test_sandbox_scale.py b/tests/harborrl/test_sandbox_scale.py
new file mode 100644
index 000000000..2e6f39e2a
--- /dev/null
+++ b/tests/harborrl/test_sandbox_scale.py
@@ -0,0 +1,36 @@
+"""A saturated image pool must not reserve unrelated global capacity."""
+import asyncio
+from contextlib import suppress
+
+from harborrl.rollout.harbor_job.sandbox_config import SandboxAdmission
+
+
+def test_waiting_binding_does_not_starve_other_environments(tmp_path):
+ async def run():
+ first = {'image': 'first', 'max_live_instances': 1}
+ second = {'image': 'second', 'max_live_instances': 1}
+ admission = SandboxAdmission({'max_live_instances': 2, 'bindings': [first, second]})
+ entered, release = asyncio.Event(), asyncio.Event()
+
+ async def waiting():
+ entered.set()
+ async with admission.lease(first, tmp_path / 'waiting'):
+ await release.wait()
+
+ async with admission.lease(first, tmp_path / 'active'):
+ task = asyncio.create_task(waiting())
+ try:
+ await entered.wait()
+ async def independent():
+ async with admission.lease(second, tmp_path / 'independent'):
+ pass
+ await asyncio.wait_for(independent(), 1)
+ finally:
+ task.cancel()
+ with suppress(asyncio.CancelledError):
+ await task
+ # Cancellation of a queued request must not leak either admission permit.
+ async with admission.lease(first, tmp_path / 'after'), admission.lease(second, tmp_path / 'also-after'):
+ pass
+
+ asyncio.run(run())
diff --git a/tests/harborrl/test_service_group_concurrency.py b/tests/harborrl/test_service_group_concurrency.py
new file mode 100644
index 000000000..3cbf4c9be
--- /dev/null
+++ b/tests/harborrl/test_service_group_concurrency.py
@@ -0,0 +1,63 @@
+import asyncio
+import threading
+from types import SimpleNamespace
+
+import pytest
+
+from harborrl.backends.slime_v032 import rollout, rollout_service_client
+
+
+@pytest.mark.parametrize('fail', [False, True])
+def test_service_groups_overlap_preserve_order_and_await_failure_cleanup(monkeypatch, fail):
+ barrier = threading.Barrier(2, timeout=3)
+ cancelled, completed = threading.Event(), threading.Event()
+ activated, failed = threading.Event(), threading.Event()
+
+ class Client:
+ def __init__(self, *args):
+ self.run_id = 'run'
+
+ def announce_serving(self, _):
+ pass
+
+ def activate_policy(self, *_):
+ activated.set()
+
+ def submit_group(self, request):
+ assert activated.is_set()
+ barrier.wait()
+ if fail and request['group_id'] == 'first':
+ failed.set()
+ raise ValueError('group failed')
+ if fail:
+ assert failed.wait(3)
+ assert not cancelled.is_set()
+ completed.set()
+ return {'result': {'id': request['group_id']}}
+
+ def cancel_run(self):
+ cancelled.set()
+
+ def load_result(run_id, request, result, *, allow_synthetic):
+ assert allow_synthetic is False
+ return [result['id']]
+
+ monkeypatch.setenv('HARBORRL_ROLLOUT_SERVICE_URL', 'http://localhost')
+ monkeypatch.setenv('RUN_ID', 'run')
+ monkeypatch.delenv('HARBORRL_ROLLOUT_SERVICE_TOKEN_FILE', raising=False)
+ monkeypatch.setattr(rollout_service_client, 'RolloutServiceClient', Client)
+ monkeypatch.setattr(rollout_service_client, 'fetch_pool_states', lambda _: [{'weight_version': '1'}])
+ monkeypatch.setattr(rollout_service_client, 'service_group_request', lambda group, *_: {'group_id': group})
+ monkeypatch.setattr(rollout_service_client, 'load_group_result', load_result)
+ monkeypatch.setattr(rollout, 'lock_policy_version', lambda _: '1')
+ monkeypatch.setattr(rollout, 'sampling_params', lambda _: {})
+ monkeypatch.setattr(rollout, 'bind_group_samples', lambda *_: None)
+ args = SimpleNamespace(sglang_router_ip='localhost', sglang_router_port=8000)
+ if fail:
+ with pytest.raises(ValueError, match='group failed'):
+ asyncio.run(rollout.run_service_groups(args, ['first', 'second'], 0))
+ assert cancelled.is_set()
+ else:
+ assert asyncio.run(rollout.run_service_groups(args, ['first', 'second'], 0)) == [['first'], ['second']]
+ assert not cancelled.is_set()
+ assert completed.is_set()
diff --git a/tests/harborrl/test_slime_v032_exporter.py b/tests/harborrl/test_slime_v032_exporter.py
index d93e84746..41ce72ef9 100644
--- a/tests/harborrl/test_slime_v032_exporter.py
+++ b/tests/harborrl/test_slime_v032_exporter.py
@@ -221,6 +221,46 @@ def test_pure_clipped_loss_numerics():
assert LOSS_SCALE_CORRECTION == 1.0
+def test_postprocess_accepts_multiple_groups_and_partial_actor_shards(tmp_path):
+ args = SimpleNamespace(n_samples_per_prompt=2)
+ groups = native_groups(tmp_path)
+ batch = export_training_batch(groups, rollout_ids=[10, 11])
+ data = convert_samples_to_train_data(args, fake_samples(batch, groups))
+ rollout_data_postprocess(args, 0, data)
+ shard = {key: values[::2] for key, values in data.items()}
+ rollout_data_postprocess(args, 0, shard)
+
+
+def test_postprocess_rejects_individual_group_overweight():
+ args = SimpleNamespace(n_samples_per_prompt=2)
+ data = {
+ "tokens": [[1, 2]] * 4,
+ "response_lengths": [1] * 4,
+ "rewards": [0.0] * 4,
+ "raw_reward": [0.0] * 4,
+ "truncated": [0] * 4,
+ "sample_indices": list(range(4)),
+ "rollout_ids": [10, 10, 10, 11],
+ "loss_masks": [[1]] * 4,
+ "rollout_log_probs": [[-0.2]] * 4,
+ "rollout_mask_sums": [1, 1, 1, 2],
+ }
+ # Total weight is below two, but group 10 exceeds its own budget.
+ with pytest.raises(ValueError, match="budget.*10"):
+ rollout_data_postprocess(args, 0, data)
+
+
+@pytest.mark.parametrize("group_id", [-1, 0.5, float("nan"), "invalid"])
+def test_postprocess_rejects_invalid_group_id(tmp_path, group_id):
+ args = SimpleNamespace(n_samples_per_prompt=2)
+ groups = native_groups(tmp_path)
+ batch = export_training_batch(groups, rollout_ids=[10, 11])
+ data = convert_samples_to_train_data(args, fake_samples(batch, groups))
+ data["rollout_ids"][0] = group_id
+ with pytest.raises(ValueError, match="rollout id"):
+ rollout_data_postprocess(args, 0, data)
+
+
def test_postprocess_rejects_drift():
args = SimpleNamespace(n_samples_per_prompt=2)
base = {
@@ -265,7 +305,7 @@ def __float__(self):
"raw_reward": [Scalar()],
"truncated": [IntegerScalar()],
"sample_indices": [0],
- "rollout_ids": [0],
+ "rollout_ids": [IntegerScalar()],
"loss_masks": [[1]],
"rollout_log_probs": [[Scalar()]],
"rollout_mask_sums": [IntegerScalar()],
@@ -402,3 +442,45 @@ def test_export_package_has_no_backend_imports():
else:
continue
assert not (names & forbidden), f"{path} imports backend dependencies: {names & forbidden}"
+
+
+@pytest.mark.parametrize('group_index, expected_advantages, expected_counts', [
+ (0, [-0.5 / 0.500001, 0.5 / 0.500001], [2, 6]),
+ (1, [0.0, 0.0], [4, 2]),
+])
+def test_legacy_export_preserves_grpo_record_semantics(
+ tmp_path, group_index, expected_advantages, expected_counts,
+):
+ from harborrl.rollout.exporters.native import export_group
+ group = native_groups(tmp_path)[group_index]
+ exported = export_group(group, 2)
+ assert exported['export_version'] == 'native-grpo-v2'
+ assert exported['advantages'] == expected_advantages
+ assert exported['reduction'] == 'sum_token_weighted_loss; no secondary whitening'
+ assert exported['synthetic'] is False
+ for ir, advantage, count in zip(group, expected_advantages, expected_counts):
+ records = [record for record in exported['records']
+ if record['identity']['trajectory_id'] == ir['identity']['trajectory_id']]
+ assert len(records) == len(ir['turns'])
+ for index, (turn, record) in enumerate(zip(ir['turns'], records)):
+ n = len(turn['output_ids'])
+ assert record == {
+ 'identity': ir['identity'], 'turn_index': index,
+ 'tokens': turn['input_ids'] + turn['output_ids'],
+ 'prompt_length': len(turn['input_ids']), 'response_length': n,
+ 'loss_mask': [0] * len(turn['input_ids']) + [1] * n,
+ 'old_logprobs': turn['logprobs'], 'advantages': [advantage] * n,
+ 'token_weights': [1.0 / (2 * count)] * n,
+ 'training_reward': ir['evaluation']['training_reward'], 'padding': False,
+ }
+
+
+def test_native_task_inspection_avoids_legacy_conversion_imports():
+ import subprocess
+ import sys
+ code = (
+ 'import sys; import harborrl.data.harbor.native_inspector; '
+ 'assert "harborrl.data.harbor.inspector" not in sys.modules; '
+ 'assert "harborrl.data.harbor.materializer" not in sys.modules'
+ )
+ subprocess.run([sys.executable, '-c', code], check=True)
diff --git a/tests/harborrl/test_webui.py b/tests/harborrl/test_webui.py
new file mode 100644
index 000000000..ddb397ffd
--- /dev/null
+++ b/tests/harborrl/test_webui.py
@@ -0,0 +1,244 @@
+"""HTTP and process lifecycle contracts without allocating training resources."""
+import http.client
+import json
+import subprocess
+import sys
+import threading
+import time
+from pathlib import Path
+
+import pytest
+import yaml
+from test_options import answers as options_answers
+
+from harborrl.options.wizard import build_config_bundle
+from harborrl.webui.cli import workspace_lock
+from harborrl.webui.jobs import Jobs
+from harborrl.webui.server import Console, make_server
+
+answers = options_answers
+
+
+@pytest.fixture
+def console(tmp_path):
+ instance = Console(tmp_path / "web")
+ yield instance
+ instance.jobs.close()
+
+
+@pytest.fixture
+def server(console):
+ server = make_server(console)
+ thread = threading.Thread(target=server.serve_forever)
+ thread.start()
+ yield server
+ server.shutdown()
+ thread.join(5)
+ server.server_close()
+
+
+def request(server, console, method, path, body=None, headers=None):
+ connection = http.client.HTTPConnection("127.0.0.1", server.server_port, timeout=5)
+ defaults = {"Authorization": "Bearer " + console.token, "Content-Type": "application/json",
+ "Origin": f"http://127.0.0.1:{server.server_port}"}
+ connection.request(method, path, body=json.dumps(body) if body is not None else None,
+ headers={**defaults, **(headers or {})})
+ response = connection.getresponse()
+ payload = response.read()
+ result = (response.status, dict(response.getheaders()), payload)
+ connection.close()
+ return result
+
+
+def test_static_and_bootstrap(server, console):
+ for path in ("/", "/app.js", "/style.css", "/lucide.min.js", "/brand.jpg"):
+ status, headers, payload = request(server, console, "GET", path)
+ assert status == 200 and payload
+ assert "frame-ancestors 'none'" in headers["Content-Security-Policy"]
+ assert console.token.encode() not in payload
+ status, _, payload = request(server, console, "GET", "/api/bootstrap")
+ assert status == 200
+ assert json.loads(payload)["catalog"]["catalog_version"] == 1
+
+
+@pytest.mark.parametrize("headers", [{"Authorization": "Bearer invalid"}, {"Origin": "https://external.example"},
+ {"Host": "external.example"}, {"Content-Type": "text/plain"}])
+def test_request_boundary(server, console, headers):
+ status, _, _ = request(server, console, "POST", "/api/configurations", {}, headers=headers)
+ assert status in {403, 415}
+ assert not console.list_configurations()
+
+
+@pytest.mark.parametrize("name", ["../outside", "/absolute", "", "a/b", "a\\b", "a b", ".hidden"])
+def test_output_paths(console, answers, name):
+ with pytest.raises(ValueError):
+ console.save({"name": name, "answers": answers})
+
+
+def test_preview_save_and_snapshot(server, console, answers):
+ body = {"name": "experiment", "answers": answers, "check_files": True}
+ status, _, payload = request(server, console, "POST", "/api/preview", body)
+ assert status == 200, payload
+ assert json.loads(payload)["validation"]["status"] == "valid_with_deferred_checks"
+ assert not console.directory("experiment").exists()
+ status, _, payload = request(server, console, "POST", "/api/configurations", body)
+ assert status == 201, payload
+ assert request(server, console, "POST", "/api/configurations", body)[0] == 400
+ status, _, payload = request(server, console, "POST", "/api/jobs", {"configuration": "experiment", "mode": "dry-run"})
+ assert status == 201, payload
+ job = json.loads(payload)
+ for _ in range(600):
+ record = console.jobs.list()[0]
+ if record["state"] != "running":
+ break
+ time.sleep(.05)
+ assert record["state"] == "succeeded", console.jobs.log(job["id"])
+ snapshot = Path(record["snapshot"])
+ training = yaml.safe_load((snapshot / "config.yaml").read_text())
+ deployment = yaml.safe_load((snapshot / "deployment.yaml").read_text())
+ assert training["output"]["root"] == deployment["storage"]["run_root"] == record["output_root"]
+ saved = console.directory("experiment") / "config.yaml"
+ assert yaml.safe_load(saved.read_text())["output"]["root"] != record["output_root"]
+ assert json.loads(console.jobs.log(job["id"])["text"])["config"]["schema_version"] == 2
+
+
+@pytest.mark.parametrize("mode", ["train", "doctor", "shell"])
+def test_live_requires_confirmation(console, answers, mode, monkeypatch):
+ console.save({"name": "test", "answers": answers})
+ def forbidden(*args, **kwargs):
+ raise AssertionError("Unconfirmed live job must not execute")
+ monkeypatch.setattr(subprocess, "Popen", forbidden)
+ with pytest.raises(ValueError):
+ console.start({"configuration": "test", "mode": mode})
+
+
+def test_process_lifecycle(console, answers, tmp_path, monkeypatch):
+ console.save({"name": "test", "answers": answers})
+ real_popen = subprocess.Popen
+ calls = []
+ def stand_in(command, **kwargs):
+ calls.append(command)
+ return real_popen([sys.executable, "-u", "-c", "import time; print('training stand-in', flush=True); time.sleep(30)"], **kwargs)
+ monkeypatch.setattr(subprocess, "Popen", stand_in)
+ job = console.start({"configuration": "test", "mode": "train", "confirmed": True})
+ assert calls[0][4] == "train" and "--dry-run" not in calls[0]
+ with pytest.raises(ValueError, match="active"):
+ console.start({"configuration": "test", "mode": "dry-run"})
+ for _ in range(600):
+ if console.jobs.log(job["id"])["text"]:
+ break
+ time.sleep(.05)
+ child = console.jobs.child
+ stopped = console.jobs.stop(job["id"])
+ assert stopped["state"] == "stopping"
+ child.wait(timeout=5)
+ assert console.jobs.list()[0]["state"] == "stopped"
+ assert console.jobs.log(job["id"])["text"]
+ recovered = Jobs(console.jobs.root)
+ assert recovered.list()[0]["state"] == "stopped"
+
+
+def test_restart_blocks_unknown_job(console, answers, tmp_path):
+ root = console.jobs.root / "abc"
+ root.mkdir()
+ (root / "job.json").write_text(json.dumps({"id": "abc", "state": "running", "created_at": "2026-10-22"}))
+ recovered = Jobs(console.jobs.root)
+ assert recovered.list()[0]["state"] == "unknown"
+ with pytest.raises(ValueError, match="interrupted"):
+ recovered.start("test", build_config_bundle(answers, output_directory=tmp_path / "out"), "dry-run")
+ recovered.reconcile("abc")
+ assert recovered.list()[0]["state"] == "interrupted"
+
+
+def test_workspace_lock(tmp_path):
+ with workspace_lock(tmp_path), pytest.raises(ValueError, match="Another console"), workspace_lock(tmp_path):
+ pass
+ with workspace_lock(tmp_path):
+ pass
+
+
+def test_symlink_cannot_escape(console, tmp_path, answers):
+ outside = tmp_path / "outside"
+ outside.mkdir()
+ (console.configurations / "escape").symlink_to(outside, target_is_directory=True)
+ with pytest.raises(ValueError):
+ console.save({"name": "escape", "answers": answers})
+ assert not list(outside.iterdir())
+
+
+def test_failed_spawn_and_log_limit(console, answers, tmp_path, monkeypatch):
+ def failed(*args, **kwargs):
+ raise OSError("injected launch failure")
+ monkeypatch.setattr(subprocess, "Popen", failed)
+ bundle = build_config_bundle(answers, output_directory=tmp_path / "out")
+ job = console.jobs.start("test", bundle, "dry-run")
+ assert job["state"] == "failed"
+ Path(job["log_path"]).write_bytes(b"x" * 100000)
+ result = console.jobs.log(job["id"])
+ assert len(result["text"]) == 65536 and result["truncated"]
+ training_log = Path(job["output_root"]) / "training/one/train-stdout.log"
+ training_log.parent.mkdir(parents=True)
+ training_log.write_text("step=1 reward=0.5")
+ assert console.jobs.log(job["id"])["training_text"] == "step=1 reward=0.5"
+
+
+def test_import_and_bad_body(server, console):
+ status, _, payload = request(server, console, "POST", "/api/import", {"text": "answers_version: 1\npreset: embedded-local"})
+ assert status == 200 and json.loads(payload)["answers"]["preset"] == "embedded-local"
+ assert request(server, console, "POST", "/api/import", {"text": "[]"})[0] == 400
+ assert request(server, console, "GET", "/../../pyproject.toml")[0] == 404
+
+
+@pytest.mark.parametrize("text", ["answers_version: true\npreset: embedded-local", "answers_version: 1\npreset: embedded-local\ntraining: []", "answers_version: 1\npreset: embedded-local\nunknown: x"])
+def test_import_rejects_invalid_answers(server, console, text):
+ assert request(server, console, "POST", "/api/import", {"text": text})[0] == 400
+
+
+def test_preview_never_starts_process(server, console, answers, monkeypatch):
+ def forbidden(*args, **kwargs):
+ raise AssertionError("Preview must not start a process")
+ monkeypatch.setattr(subprocess, "Popen", forbidden)
+ assert request(server, console, "POST", "/api/preview", {"name": "offline", "answers": answers, "check_files": True})[0] == 200
+
+
+def test_local_stop_reaches_descendants(tmp_path):
+ import os
+ import signal
+
+ from harborrl.config import ROOT
+ script = '''
+import os, signal, subprocess, sys, time
+from pathlib import Path
+from harborrl.deployment.training_target import LocalTrainingTarget
+root = Path(sys.argv[1])
+worker = "import signal,sys,time; from pathlib import Path; signal.signal(signal.SIGINT, lambda *a: (Path(sys.argv[1]).write_text('stopped'), sys.exit(0))); Path(sys.argv[1]+'.ready').touch(); time.sleep(30)"
+parent = "import subprocess,sys,time; from pathlib import Path; child=subprocess.Popen([sys.executable,'-c',sys.argv[1],sys.argv[2]]); Path(sys.argv[2]+'.pid').write_text(str(child.pid)); time.sleep(30)"
+target = LocalTrainingTarget([sys.executable, '-c', parent, worker, str(root / 'worker')], cwd=root, env=os.environ.copy())
+target.launch()
+(root / 'group').write_text(str(target.child.pid))
+sys.exit(target.wait())
+'''
+ env = {**os.environ, "PYTHONPATH": str(ROOT)}
+ proc = subprocess.Popen([sys.executable, "-c", script, str(tmp_path)], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ try:
+ for _ in range(600):
+ if (tmp_path / "worker.ready").exists():
+ break
+ time.sleep(.05)
+ assert (tmp_path / "worker.ready").exists()
+ proc.send_signal(signal.SIGINT)
+ proc.wait(timeout=10)
+ for _ in range(100):
+ if (tmp_path / "worker").exists():
+ break
+ time.sleep(.05)
+ assert (tmp_path / "worker").read_text() == "stopped"
+ finally:
+ if (tmp_path / "group").exists():
+ try:
+ os.killpg(int((tmp_path / "group").read_text()), signal.SIGKILL)
+ except ProcessLookupError:
+ pass
+ if proc.poll() is None:
+ proc.kill()
+ proc.wait(timeout=10)
diff --git a/tests/test_consistency.py b/tests/test_consistency.py
new file mode 100644
index 000000000..04146beb5
--- /dev/null
+++ b/tests/test_consistency.py
@@ -0,0 +1,81 @@
+"""Dependency-free tests of consistency evidence and admission decisions."""
+import struct
+
+import pytest
+
+from harborrl.consistency.config import require_raw_sampling, validate
+from harborrl.consistency.metrics import merge, statistics
+from harborrl.consistency.report import assess, decide
+from harborrl.consistency.sglang import check_pool, score
+from harborrl.consistency.slime import evidence_key
+
+
+def test_weighted_statistics_ignore_padding_and_tp_duplicates():
+ settings = validate({'mode': 'enforce', 'max_abs_delta': 1})
+ a = assess([([-1], [-2], [1])], settings)
+ b = assess([([-2, -2, -100], [-2, -2, -1], [1, 1, 0])], settings)
+ duplicate = {**a, 'contribute': False}
+ report = decide([a, b, duplicate], settings, phase='test')
+ assert report['status'] == 'passed'
+ assert report['metrics']['token_count'] == 3
+ assert report['metrics']['mean_abs_delta'] == pytest.approx(1 / 3)
+ assert report['metrics']['p99_abs_delta_upper'] == 1
+
+
+@pytest.mark.parametrize('rows', [[], [([-1], [-1], [0])], [([float('nan')], [-1], [1])],
+ [([-1], [-1, -2], [1])], [([-1], [-1], [2])]])
+def test_structural_errors_fail_audit(rows):
+ settings = validate({'mode': 'audit'})
+ assert decide([assess(rows, settings)], settings, phase='test')['status'] == 'failed'
+
+
+def test_thresholds_are_explicit_and_sampling_is_raw():
+ with pytest.raises(ValueError):
+ validate({'mode': 'enforce'})
+ with pytest.raises(ValueError):
+ require_raw_sampling({'temperature': .7, 'top_p': 1})
+ settings = validate({'mode': 'audit', 'max_abs_delta': .01})
+ assert decide([assess([([-1], [-2], [1])], settings)], settings, phase='test')['status'] == 'warning'
+
+
+def test_serving_evidence_survives_float32_conversion():
+ q = [-1.123456789]
+ converted = [struct.unpack('!f', struct.pack('!f', q[0]))[0]]
+ assert evidence_key([1, 2], q, [1]) == evidence_key([1, 2], converted, [1])
+ assert evidence_key([1, 3], q, [1]) != evidence_key([1, 2], converted, [1])
+
+
+def transport(url, payload=None):
+ if payload is None:
+ return {'weight_version': '1'}
+ assert payload['sampling_params']['max_new_tokens'] == 0
+ assert payload['input_ids'] == [10, 11, 12]
+ return {'meta_info': {'weight_version': '1', 'input_token_logprobs': [[None, 10], [-1., 11], [-2., 12]]}}
+
+
+def test_teacher_forcing_exact_response_boundary():
+ assert score('http://engine', [10, 11], [12], '1', transport=transport) == [-2.]
+ with pytest.raises(ValueError, match='version'):
+ score('http://engine', [10, 11], [12], '2', transport=transport)
+
+
+def test_engine_canary_does_not_dilute_bad_engine():
+ settings = validate({'mode': 'enforce', 'max_abs_delta': 2, 'max_mean_abs_delta': .6})
+ reference = {'weight_version': '1', 'cases': [{'prompt_ids': [10, 11], 'response_ids': [12], 'logprobs': [-1.]}]}
+ states = [{'endpoint': 'http://bad', 'healthy': True, 'weight_version': '1'},
+ {'endpoint': 'http://good', 'healthy': True, 'weight_version': '1'}]
+ def serving(url, payload=None):
+ result = transport(url, payload)
+ if payload and 'good' in url:
+ result['meta_info']['input_token_logprobs'][-1][0] = -1.
+ return result
+ report = check_pool(states, reference, settings, transport=serving)
+ assert report['metrics']['mean_abs_delta'] == .5
+ assert report['status'] == 'failed'
+
+
+def test_histogram_merge_is_token_weighted():
+ parts = [statistics([([-1.] * n, [-1. - delta] * n, [1] * n)]) for n, delta in [(1, .1), (9, .01)]]
+ result = merge(parts)
+ assert result['mean_abs_delta'] == pytest.approx(.019)
+ assert result['p95_abs_delta_upper'] >= .1 - 1e-12
diff --git a/tests/test_consistency_torch.py b/tests/test_consistency_torch.py
new file mode 100644
index 000000000..0f81e6660
--- /dev/null
+++ b/tests/test_consistency_torch.py
@@ -0,0 +1,31 @@
+"""Distributed rejection tests; optional Torch dependency."""
+from pathlib import Path
+
+import pytest
+
+from harborrl.consistency.config import validate
+from harborrl.consistency.report import assess, distributed_gate
+
+torch = pytest.importorskip('torch')
+
+
+def rank_failure(rank, rendezvous, root):
+ from datetime import timedelta
+
+ import torch.distributed as dist
+ dist.init_process_group('gloo', init_method=f'file://{rendezvous}', rank=rank, world_size=2, timeout=timedelta(seconds=30))
+ try:
+ settings = validate({'mode': 'enforce', 'max_abs_delta': .01})
+ part = assess([([-1.], [-1. if rank == 0 else float('nan')], [1])], settings)
+ with pytest.raises(ValueError, match='rejected'):
+ distributed_gate(part, settings, Path(root) / 'failure.json', phase='injected-rank-failure')
+ (Path(root) / f'rank{rank}.ok').write_text('rejected')
+ finally:
+ dist.destroy_process_group()
+
+
+def test_one_rank_error_rejects_all_ranks(tmp_path):
+ import torch.multiprocessing as mp
+ mp.spawn(rank_failure, args=(str(tmp_path / 'rendezvous'), str(tmp_path)), nprocs=2, join=True)
+ assert (tmp_path / 'rank0.ok').exists()
+ assert (tmp_path / 'rank1.ok').exists()
diff --git a/uv.lock b/uv.lock
index d5e9dd73d..4efd07ea6 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,34 +1,976 @@
version = 1
-revision = 3
+revision = 5
requires-python = ">=3.10"
+resolution-markers = [
+ "python_full_version >= '3.14'",
+ "python_full_version >= '3.12' and python_full_version < '3.14'",
+ "python_full_version < '3.12'",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.14.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6c/4c/bdccd81e9ee225b69c60e7766c9a5b05364f118f4d383713b89a682d772d/aiohttp-3.14.5.tar.gz", hash = "sha256:5558a7f5a05af9ecf744af91e5baefc436f93c9333e656c27ec253f9a6bbe178", size = 8078052, upload-time = "2026-10-11T01:05:12.408Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ca/82/f7f0652e3a845e095d8505a2807a0fe5782abb71f99b2f516c93ff0be492/aiohttp-3.14.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef692a24087a699c0a4a26af45e746e0c1eae2116f6d8a5ff91d8aae2b867b45", size = 834351, upload-time = "2026-10-11T00:59:07.668Z" },
+ { url = "https://files.pythonhosted.org/packages/95/c5/10cb0a5195bc3ec05a74ee494f8431a15e559800f2e869f3526ad4a16f04/aiohttp-3.14.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1220353657ad49493551f089ce02f1a348fd57ffd585bfec77f2f3c4fe3a7346", size = 560663, upload-time = "2026-10-11T00:59:09.853Z" },
+ { url = "https://files.pythonhosted.org/packages/32/dc/b7fefdd7dd64d080da2d69d609344191cc94b0e2eba70200c371014c9b36/aiohttp-3.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:330900acd0dc4cb8b27f9c127fbaad770964845338493e7906ae3822e82dbf8d", size = 553263, upload-time = "2026-10-11T00:59:11.5Z" },
+ { url = "https://files.pythonhosted.org/packages/61/5f/3271cad1b34347e8bf27abee20d501ebdc56cec97e8ae59cd3fabb23d917/aiohttp-3.14.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0684952aeae1f5dbfe02d46039338513b94009baecd15d8e4098a357c4c4a2a6", size = 1843813, upload-time = "2026-10-11T00:59:13.143Z" },
+ { url = "https://files.pythonhosted.org/packages/67/af/2011b30fe61889af112ad380e6fd44a93cca4b51296f612cac666c177882/aiohttp-3.14.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d94e44be379e569758fee8a9a58431cfc3c2598c708b92b1cfe96c66b4c94aef", size = 1811243, upload-time = "2026-10-11T00:59:15.091Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/ba/72f3ac523ed48d38cdfb169f1b2a2e93ce06d9fcf4f46d0f5692e9539bcc/aiohttp-3.14.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5af42135fdfebdadbc2bcd9c0842a48ccf0d62794c36a260b21dc4b94d1e0119", size = 1898815, upload-time = "2026-10-11T00:59:17.224Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/69/bbf24f3b555ea1befa7db1e2510c728c8b0e80e61ce360beb7d94bc562b7/aiohttp-3.14.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f07fe3ac408d8b3f768be471dc3f56d43843c47d97c66120534467a15ead197", size = 2022390, upload-time = "2026-10-11T00:59:19.244Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/38/01f5fc732d5373d6c099db0343279cd4e6607287d336bab6ff6ae9085a0d/aiohttp-3.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f375db73a39f5cf83696d500e21a67f418dc9a988955756f254be8f03b7b3651", size = 1856329, upload-time = "2026-10-11T00:59:21.19Z" },
+ { url = "https://files.pythonhosted.org/packages/15/85/1f86fbd8fe34ad447c63e679404a1d5be435fe256a9d281b791fc1ed0d35/aiohttp-3.14.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8df7d481654ac96fe1ba9a02a9f67770fdd367823e0d5ef01b922725c4bd2cfa", size = 1712763, upload-time = "2026-10-11T00:59:22.982Z" },
+ { url = "https://files.pythonhosted.org/packages/39/1e/ced51cf47e427e7aa6142c3bf6f33022472703bc9061289329532d3c40c0/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e95c8def4b81c5d68d5cf1f54c07acd7c0d2577af244e5b6da802120825737c6", size = 1812125, upload-time = "2026-10-11T00:59:25.032Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/f1/9610b4e263b554ca4191501ba72b952efe06556b647da74887abc5d41049/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2ebb54b3f932210503072f09974b4fb574d823e497a944adfdcd140a6a00255", size = 1829550, upload-time = "2026-10-11T00:59:27.078Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/8a/1d1fb27cf9fa99b13133de33044c4d10d7705be0d4173d6d56abad1b67f9/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:038c2c7e8caa26b6c8423779b5eaf1893904048a512c19b32fe841ffa5592b50", size = 1868179, upload-time = "2026-10-11T00:59:29.315Z" },
+ { url = "https://files.pythonhosted.org/packages/24/9f/3cfafa86ff025623dcca0d30335db7b8a6f3fa348148441208147e827e04/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b7806e804889231b0e06469fd4a5c06313d1c0a3377322b6d9237fa5e0fe4167", size = 1697721, upload-time = "2026-10-11T00:59:31.161Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1d/86439a23bf32296e715d8c75535f0cd701821d36719324baef65bcd6af9e/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9bab2045550c4fe0f7baf89574db1b455c195750702ba96fef1f16972b146617", size = 1887000, upload-time = "2026-10-11T00:59:33.329Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/0f/6044f2d02b3874966d84cd119f9b8eda46aa2991230078aea4dd17750cc9/aiohttp-3.14.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96a2e584f0b9ed8f1fa33211397dcf67bb7069866402cb405d191c2f0defb9a3", size = 1842020, upload-time = "2026-10-11T00:59:35.384Z" },
+ { url = "https://files.pythonhosted.org/packages/18/64/b49d2a34428ff937631b07dde74d5879a7a2c8559aab39331d40769e8cb6/aiohttp-3.14.5-cp310-cp310-win32.whl", hash = "sha256:602c1e9b718a3275c580149f947e7fac65044c0a20e599553fb12e9700da9eca", size = 489282, upload-time = "2026-10-11T00:59:37.061Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/7c/1114e723f1c4a65597820b605a0ddcd7f3b28fdfde3ac8d520d517ca582c/aiohttp-3.14.5-cp310-cp310-win_amd64.whl", hash = "sha256:bea559ad70218d230663e4210875735076a9bfea5994cef34a55a25faeaf2544", size = 512021, upload-time = "2026-10-11T00:59:38.7Z" },
+ { url = "https://files.pythonhosted.org/packages/48/97/e08c646f4bad63d77876b24591d013d7f33f02fda7c931eafe3224f38467/aiohttp-3.14.5-cp310-cp310-win_arm64.whl", hash = "sha256:dca3fa8d8a0a26679862eccb0b1a9151b2b9f1cd2c212e7a6335778faaff5833", size = 493016, upload-time = "2026-10-11T00:59:40.525Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/f3/8997f18890a92c79f77fcfbb4f78ca17c2d4ab109e9eb6d31b4e9de194a0/aiohttp-3.14.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d51db97c96384fbfcaf8f4c65922183a68b94f891c3c10c862ef5f6df2adbb1f", size = 832358, upload-time = "2026-10-11T00:59:42.195Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/42/084651e9efb5cadb99265f786df60f2a7b353cead7bfd2c87003cb4867ad/aiohttp-3.14.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae53924aa853a7a2ca20ed4142c7c6b56338e4d4cd999e2980075b9efc2e257a", size = 559636, upload-time = "2026-10-11T00:59:43.677Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/fe/92838944e601f0fe195fd3f3ada37e29fbee3d19bfd323fe15937ec79d36/aiohttp-3.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2c473a355f9239efcb72c92d5abfd8fcdb0cc78c8e9af607e72ca12dbb36593", size = 552588, upload-time = "2026-10-11T00:59:45.592Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b8/dd9b95c5c20ceae1b47738e656bd79e9883e3117a9cc8b143901eb604e9a/aiohttp-3.14.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e8fa6644e541fcd7e02430588c7fc93b602c1778ea0bc345505db76b61cfb4", size = 1928224, upload-time = "2026-10-11T00:59:47.294Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/47/70010cd2ba8746968d53d433ff328c33f06a66c99da5f3de759c64a6b2eb/aiohttp-3.14.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:579f97d5120f2971876d2ddca2968135f6944d00c44c3a6590ad7d86ca9b403f", size = 1877674, upload-time = "2026-10-11T00:59:49.472Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/a1/b026f071dbd87f0bdba86b92d47e46f3899e3ad7143b6e0fe7e7887090b4/aiohttp-3.14.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:24409db442e2fb6e766bc7f3943851a8381dec3098140e43bb2e843b79e31b12", size = 1985850, upload-time = "2026-10-11T00:59:51.296Z" },
+ { url = "https://files.pythonhosted.org/packages/36/d1/f7b6f6f8c3cb5a71b53baeddc2e70c224c28b12a23a55c42994c504019b7/aiohttp-3.14.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5e8f97c0488ffda3082766ac0f2c8150a9a58c4d05788330e479cfd449b37939", size = 2105471, upload-time = "2026-10-11T00:59:53.191Z" },
+ { url = "https://files.pythonhosted.org/packages/42/74/2a2b22953de15c6c8a80debf26db3047e4e8a5a5db6dd7c2c6c01eec0605/aiohttp-3.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50a195903119008fe9cc68710535eb37f556ffffd6a7759afe70a2c145587045", size = 1929915, upload-time = "2026-10-11T00:59:55.609Z" },
+ { url = "https://files.pythonhosted.org/packages/33/ad/f80e8d33933d0eacd0217efa3b7fb32c9f48ed480ed0db53cf9948bf474f/aiohttp-3.14.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0133c3c3b54a0bf1e71fa5c1ad95c93f07fd54e24ef1fe182f5122e1573d2bf1", size = 1773832, upload-time = "2026-10-11T00:59:57.611Z" },
+ { url = "https://files.pythonhosted.org/packages/04/a4/0273d239f3e3bb69438f209c64c82e1f98dc60d5db264795e41f7dd05dcb/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c172db893e516e1358e65a95ee20b7ce7173963eefe318b6ab2a2220688b999e", size = 1901001, upload-time = "2026-10-11T01:00:00.605Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ed/7dd439d26c654645bc34ca3c9a822fc11c1c51801fed1cefdfe1cc41c1f6/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f2a7966bda23dd85051f1661ce0ace38d6890e05ec6c357ecae9d2479cba377e", size = 1907218, upload-time = "2026-10-11T01:00:02.699Z" },
+ { url = "https://files.pythonhosted.org/packages/32/dd/86249c3b8248562a17fd3e22ee0c164378d65766f01aceef62cf52783710/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4887d130a7bbfed3a85493bb5a25e5b5b558d40c1d986dd16970d2bb26d63793", size = 1958650, upload-time = "2026-10-11T01:00:04.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/0b/ca4d53d68f683ce195807fd0aeb063bf6d1c6b39343fff234c9524e8d5a9/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:225c579c23b68b343cccea27a7e06e3bd8ec23a09c30b427eb3f1e4ca6239b20", size = 1760057, upload-time = "2026-10-11T01:00:06.978Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/42/005437ffd7fa56c2ce3347add40404b654a32754ce91f2e2a14b9e5ab2bc/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ab52d8f1fc1b64821c1fbad64a647ed6203627004059a6d1ed4f0858a1499703", size = 1978081, upload-time = "2026-10-11T01:00:08.868Z" },
+ { url = "https://files.pythonhosted.org/packages/03/71/3a5b66fe1b7b23d3c6524350817ed55de5feb7fbb4cccba7cbe044d118dd/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb131d775a1573c1aee66656bd78b023577bbdb6cb8349a07773bd4f73e68a6e", size = 1922954, upload-time = "2026-10-11T01:00:11.035Z" },
+ { url = "https://files.pythonhosted.org/packages/69/84/d08a554f281d42fd7c6b2b6dd7738f3e3a7a9f657ddd87e11ca2091cdfe3/aiohttp-3.14.5-cp311-cp311-win32.whl", hash = "sha256:e87046c8ff77a8decdb6a41d8ab25824b47531b2da933aeab0c1e21c7acff329", size = 488101, upload-time = "2026-10-11T01:00:12.933Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/15/52b5e65f02b33686ae49f1518548ae4f5a7adaf31d9d3c54fa12a143137b/aiohttp-3.14.5-cp311-cp311-win_amd64.whl", hash = "sha256:6f275c11d1aa6d4c458e05a68be084efe3c55a113d99e3f46a318098e52948fc", size = 512575, upload-time = "2026-10-11T01:00:14.743Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/b9/bb75012635f3defb0f7cbb7c4ae391e36bb9cb3fbdca3b9944e2ebebf743/aiohttp-3.14.5-cp311-cp311-win_arm64.whl", hash = "sha256:b032a0023eb41d768ce77d83210ab2a3c389bc0b09313273c7e1eca48c10a755", size = 493179, upload-time = "2026-10-11T01:00:16.961Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/94/6ba86efddcb616c811b40e6a0dfdd862738f647e4e3860a961075d5e9ed8/aiohttp-3.14.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df37b620684e19b5e25724412518ccafc3b1a49cdac706fdbd2f983fad943450", size = 817499, upload-time = "2026-10-11T01:00:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/e1/7bca6d84dabd228aa8eb4b7f9feac2586aaa9be5505d7d65bf287a615c43/aiohttp-3.14.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef60869969180ec2464f1349aff07138ae35ca2200f0946cb3552e49e8f301a8", size = 549467, upload-time = "2026-10-11T01:00:20.854Z" },
+ { url = "https://files.pythonhosted.org/packages/64/91/11b89f45ca486252dd67dd5f3231fec04bf5518da39a95cb3997619f17fb/aiohttp-3.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d079c0a0135c36e7beb6f1c88087c8f108dc5891cdd0b5eafa778421bda70ed2", size = 548067, upload-time = "2026-10-11T01:00:22.659Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ff/c6615806c14aab34f82b9424ccde8ce6e417315fd57ce1c5b4d4747888e1/aiohttp-3.14.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abfda5cb094a829f7bc25216a32f7db2e85cc65bd59910f8e7b40b3d9b224764", size = 1896245, upload-time = "2026-10-11T01:00:24.638Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b2/25a8c971ae6a92c8394d77e42422d7f38989e05cf41a5ceb92d73d67ab7e/aiohttp-3.14.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9cc882cf8619109583c906b4d4a85d6a111a98afa34b7a450d1e08118d016820", size = 1872180, upload-time = "2026-10-11T01:00:26.838Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/d5/99f93ea36cc5205e47c1e5a803e087f2ad21b5430b5db2e942cb6e988a37/aiohttp-3.14.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7457580535e019e1247ea35d6a02bf081ad30c26d0cbc210c93f6c3ab67a0835", size = 1937261, upload-time = "2026-10-11T01:00:28.74Z" },
+ { url = "https://files.pythonhosted.org/packages/40/a6/9ac9c9e6695040bd73d2584a1b59a9388f6433c76d5294a7bf591e21ffe5/aiohttp-3.14.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c5ed596aedb9c42afd3fe0aae3117725378ac73d2cc5ddc735056fbdb96c5d02", size = 2064994, upload-time = "2026-10-11T01:00:30.623Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e4/aa172eb534b7f02f1f8ff1c3213347eaf3cf218db91a727c6863c22f1035/aiohttp-3.14.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20f085697d7e911f1f73c43ed03fafbed1e7121797e2eb5428efa80398060584", size = 1919630, upload-time = "2026-10-11T01:00:32.548Z" },
+ { url = "https://files.pythonhosted.org/packages/06/7d/4eedafc5bababa8932636c141e346806966eade12c0b7e5946d43bf8218b/aiohttp-3.14.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:74b0a9c8270f9b0a11410e124ff8d4f18bfc1f1837440ec84da5ae7b50927b5d", size = 1710016, upload-time = "2026-10-11T01:00:34.471Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/94/eee018537ba19da0ceb2ac79cab83faed4ac49568e08376e2799043f2538/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:19e2ba471507c34f8252402ab50f5ab512398b9ea8c8f1cb26beb3f75793ba30", size = 1858112, upload-time = "2026-10-11T01:00:36.581Z" },
+ { url = "https://files.pythonhosted.org/packages/68/76/354653a306547238f3427283905972d796ba7c292ba9977abec9f2b6f260/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d418ce2af40c6bb685b3f663e9e8de27cb0a22431d8e88a167348d7f01878073", size = 1896371, upload-time = "2026-10-11T01:00:38.478Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/ec/63e8c7136b570e356345ad3174e3820fdc973ea10712cb6c649bf875755a/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:70cb4008ac2ed1e0ca9e824deb4b53d3aa0d939109698ebf1e723a84337bd794", size = 1905836, upload-time = "2026-10-11T01:00:40.527Z" },
+ { url = "https://files.pythonhosted.org/packages/57/d8/11365bda144b127928cd42533d0eff78a55613c9e28f81941bd6630ea887/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a23fe35d776bc03cb495938b9594450d047e3bc08c5255315a82323e9cb7d2dd", size = 1696534, upload-time = "2026-10-11T01:00:42.686Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3d/82df0461b18e00b2998f205c03e0d3010222478c43640aceb8e03dcd7e8f/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3e0eb43bed3c6801a6cee315195377789e90b2a72c2277a475b578535312488d", size = 1933970, upload-time = "2026-10-11T01:00:44.71Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ad/6ddfe0aacd931c17b53533336d97e9d11a98b96d6ae815a9da0b19f82ccf/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be7dd397d64ca3e1869626fa9318aaebb54b7bf93bc72d7a205448d83e4f748", size = 1899501, upload-time = "2026-10-11T01:00:46.629Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8e/189bdd9ae4793059bb09f6dc880f211a6c6c7859cebcbf33912dbfab7dd7/aiohttp-3.14.5-cp312-cp312-win32.whl", hash = "sha256:eb324e2009fb54db30a071dad7caf6998ee2879c4704007efb244514dad1fec1", size = 480370, upload-time = "2026-10-11T01:00:48.468Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/ce/1f08114679d49655b30a6e0a29858375c94b82c1de1a0bd0a20c2fee8b02/aiohttp-3.14.5-cp312-cp312-win_amd64.whl", hash = "sha256:2cc38a4f2b516bef1714e690df87a0e043faf1a7693c82d860091684453d5111", size = 506141, upload-time = "2026-10-11T01:00:50.272Z" },
+ { url = "https://files.pythonhosted.org/packages/79/d4/c7b4f60b16a1b7e43249fa9031ae05e7e8c341ba4b7d1866f914dafeaa0e/aiohttp-3.14.5-cp312-cp312-win_arm64.whl", hash = "sha256:a63afd1f757de949028387e65a7127b61ad0f775432dbb0e62816ae619fe69ac", size = 488108, upload-time = "2026-10-11T01:00:52.321Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/e1/2841e020ebb7aefae5513586193e011e06313d9a6bdbd296622afbbce204/aiohttp-3.14.5-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:9ad7e6aa38c20da1be697874349c4c273c8a03b7887169665081706398d0439a", size = 538932, upload-time = "2026-10-11T01:00:54.3Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/a6/7fb8ea8fe96bcc7b7c7a36d10f021d99d01a8dc8a4b3f0ddacecfad9a80e/aiohttp-3.14.5-cp313-cp313-android_24_x86_64.whl", hash = "sha256:f59c7673465908cbe506117176156c127f29f917677afceada34957179221d91", size = 553329, upload-time = "2026-10-11T01:00:56.442Z" },
+ { url = "https://files.pythonhosted.org/packages/de/64/d056e3c27647dc25af1a592cf356245382ea7c808171b9dac7677afedfc8/aiohttp-3.14.5-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1b5416552740edf07234cc9437d0706f2acb67b93c198670b1a68e1b2b587dec", size = 521367, upload-time = "2026-10-11T01:00:58.345Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/e4/95226147e11d4db916fd1d495dcf85af8e3816e38333e42718241196e848/aiohttp-3.14.5-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:43351bdb5e4c3cb7d1772368e988534e869a74db7778079a83782c11c69535c7", size = 528322, upload-time = "2026-10-11T01:01:00.211Z" },
+ { url = "https://files.pythonhosted.org/packages/17/cd/1d3c9192cafdb51cad62b2d3ded96cff9cc8af51893210aadd448a325389/aiohttp-3.14.5-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:c8c4478bef6d57fcfda15dae461ea3c9f06aa7b257c58df3f2300174ccbb185a", size = 539874, upload-time = "2026-10-11T01:01:02.06Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/0c/dfa33aecc7d4d1dc75e05248f5eac5a0edf4d09e7b44d93ab62529b0c1db/aiohttp-3.14.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c2c30484dd1417ef98b51021ffa2cc0d7f3c78918adaaaab7e70817335ab3e02", size = 813836, upload-time = "2026-10-11T01:01:04.01Z" },
+ { url = "https://files.pythonhosted.org/packages/33/17/4a63738052d20567d55529d6daa1b9480d906fd52930fbcf6d3fbed618f0/aiohttp-3.14.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dab9ac5a67c8d1f070c00fa8fccb7cbd1b8dcc1a8d6b42f37540df9b3d4cc603", size = 548070, upload-time = "2026-10-11T01:01:06.035Z" },
+ { url = "https://files.pythonhosted.org/packages/15/e5/b57e58695a757fd4c02497c033fced96a69c631b13866c43d336530c9670/aiohttp-3.14.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e1cc2bfaee8c214f06080a7c7d5772419b8a1108e8e5349236189811823fb02a", size = 545786, upload-time = "2026-10-11T01:01:07.817Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/68/8c2c67a3aedf46e00f3c42f04fbc6983de80d4ed5786151e33681ba45883/aiohttp-3.14.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74efb69332b85675b1eabd760a8cfc2e2cf42c60607c66f88014c1bdfb40942d", size = 1886492, upload-time = "2026-10-11T01:01:09.834Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/4b/74aab5e8d28c62e8f795b4fe8f38cf5586fd264a9a27bd2141ef6490333d/aiohttp-3.14.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42b5e616946dbaf505e2bff18c9af2cd4ef9e7ef300ee58a6e951a5b7cf147ae", size = 1870057, upload-time = "2026-10-11T01:01:12.045Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/f7/eafc3b1988302b1815d9fd4a21071be5c360d616c0a430d02fd92dc97688/aiohttp-3.14.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f9033b43f511f27547c557dcaba0177649e10a3725336ccd2cce0fdc1dc4850d", size = 1925119, upload-time = "2026-10-11T01:01:14.09Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/04/78d8f294f74dd570f3898ff20402349fce524176045df98ba727d6846a68/aiohttp-3.14.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15a310d3c71398e3d7bfc93a1a73fbe664315cd9e9016b8efc1cff85eeab7155", size = 2053137, upload-time = "2026-10-11T01:01:16.344Z" },
+ { url = "https://files.pythonhosted.org/packages/32/51/395d225ef36f5a50d8e548dcd3141bfdbcd31fb6eed859022c573d2c4d66/aiohttp-3.14.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ffa3a523a36d8628f98c06492ae16a31a23d14c0b4ec721757b477319f656d6", size = 1911038, upload-time = "2026-10-11T01:01:18.653Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/a4/2aec1aa06d82e8a244843b5dae31d78061e5e76744270a86dd0ee051c889/aiohttp-3.14.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5fb6a6e919bfb703227bc1ce6579281b84b1a2ba57deb9794dfdbec7dcd1e40c", size = 1702205, upload-time = "2026-10-11T01:01:20.904Z" },
+ { url = "https://files.pythonhosted.org/packages/16/27/6051bfde7b6f418f70edd60d655fa426abb3355fa0981764739d87ecf160/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:43e1b7994a8b038125f722bff07492ef501110722c2727c408995d9fb864c421", size = 1848588, upload-time = "2026-10-11T01:01:22.918Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/44/55efc06fc26c4e6e1c095f231b4c222bf2d86d64a8eebbc24b2bb5958ea8/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:5f3e96071686755d9cd3600c3880183eb94b012178e92746d68101800f0ed8a3", size = 1887540, upload-time = "2026-10-11T01:01:25.278Z" },
+ { url = "https://files.pythonhosted.org/packages/48/dc/1502bfdc2a65760d386ac6a00090b0addaa8a3c9c60b3f8127fad3a9afb2/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cd88b01f3d37b7a2a34f91d98f14720206f1ea3d540843fab2d649dd5fb91fec", size = 1895030, upload-time = "2026-10-11T01:01:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/93/7e/44174bb6288264418c9eec07a5e35180969c0d5a796c7af544db3cb8a33a/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f2ed8b64dc0c651c0f5a9c926777719770021251b8f336d97c4b80b660836ce1", size = 1693244, upload-time = "2026-10-11T01:01:29.39Z" },
+ { url = "https://files.pythonhosted.org/packages/47/dd/b507d64e50db23888582fff08eda13998b12f9dea70c072edaac19218380/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a9918e58faf62ba2c7147927d06057aec78f42475aff5048047ec47e7265a600", size = 1926308, upload-time = "2026-10-11T01:01:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4b/5b51b4f63e3f2793151f4aea49c48fe1e00baeb7cec9c7a206de499f8de0/aiohttp-3.14.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:42f320d4a5b00b9af0bddcfec5407dc6f2d9816f006b2f79ebbaa31f16895df3", size = 1888406, upload-time = "2026-10-11T01:01:33.715Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/13/d5e818a5eaba9f822016727299f41c0e1075799f82a6433ec508a93ab867/aiohttp-3.14.5-cp313-cp313-win32.whl", hash = "sha256:3ae800a20947e2c2e53088047d021e6bf7d51560cc49f6a0737a1f79d2e3a13c", size = 479970, upload-time = "2026-10-11T01:01:35.677Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/d0/8eca2c65aa467320990d78fb2005f38ed3588944280c39ff9deb6423fef1/aiohttp-3.14.5-cp313-cp313-win_amd64.whl", hash = "sha256:d05e94cdfe0d15d0206f970722d2554780ce562787b21b218b275447f8751319", size = 505985, upload-time = "2026-10-11T01:01:37.574Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/f8/4cdd65305d2fca14b886bea9ed2abb1fe726287872524e56e3d26692b47d/aiohttp-3.14.5-cp313-cp313-win_arm64.whl", hash = "sha256:f001b571ead90ca1770f1e616db255351a1703317f20374c361ef22f12c06d09", size = 487629, upload-time = "2026-10-11T01:01:39.477Z" },
+ { url = "https://files.pythonhosted.org/packages/43/be/3184a1d34a8be665569eadb7e9e764b4629e4f3413e241cb2e4d6fecf3b3/aiohttp-3.14.5-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:939042d5cda21d41a6f512e7cc8b8e33a2aebff863352251da495fbd91b673b5", size = 540673, upload-time = "2026-10-11T01:01:41.354Z" },
+ { url = "https://files.pythonhosted.org/packages/60/2a/d35f3ba4cf157b072e3b674bf9983047ca5ea5173c995d32d877e1191d36/aiohttp-3.14.5-cp314-cp314-android_24_x86_64.whl", hash = "sha256:6da32b5ff3fd78d244e37300463434c7145162bfd2b6e9e915ab164da37f7343", size = 554087, upload-time = "2026-10-11T01:01:43.719Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/d2/61a33880ca4eaca95a9c60ca3f6beed15555af1028652dfaac601627787b/aiohttp-3.14.5-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1b438b73c38111818d0c9d6a5c2bfed8584c8e503a49ef085d70e874ec846738", size = 522613, upload-time = "2026-10-11T01:01:46.154Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/de579b299d2225dc2c6fd99d579d91f16c02a913fb5af9a3cf2fe9bd88ba/aiohttp-3.14.5-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:755933b107ea7a6a9ac916f635a70595a5b1a32fac10a8ff0b9f2ab88555550c", size = 529682, upload-time = "2026-10-11T01:01:48.477Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/4a/ddb923564e15e053b6e060b0036e1694dcadcb13aa476c5a87dcad20e336/aiohttp-3.14.5-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:b3cc509327c7b27f6f4727a8830f4004f6df7766e179f2f4b8e54e65c0bec5d3", size = 540120, upload-time = "2026-10-11T01:01:50.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/36/a640fbecaa53727a5900b892bdbe17b5f3e8cc88903e22864fe41b654def/aiohttp-3.14.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7bd8ac754ebd6733a3e2a0dd1674c4d8ab086196803fd8dcd776f07b4e2607d9", size = 818795, upload-time = "2026-10-11T01:01:52.665Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b6/d52ca608859e271b5fa7944074802dc45f60e52c318a4ddc34edbf73586e/aiohttp-3.14.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e724a7b6091f0b1ac064f9d1b15ff9ec52e6033a86cdae649e5f086e32a3c0db", size = 549667, upload-time = "2026-10-11T01:01:54.65Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/b5/05b8ac39a76ff4bca89f42a4c2471560c71f894ff6e4bc16158c951874e3/aiohttp-3.14.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c32e26310cc10e547f53cd13d39a369034f69dcb7d749d5cb0e5f67bc196b6ba", size = 548922, upload-time = "2026-10-11T01:01:56.547Z" },
+ { url = "https://files.pythonhosted.org/packages/19/b0/5aa186d56ce2334dabe29b70bd99dc8ae926ee44184c0de64a53d415a4f3/aiohttp-3.14.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1eb8167961ec4dfcc8cb9dd50bd0ee72519f7ef496be95203e49e27b01618382", size = 1882422, upload-time = "2026-10-11T01:01:59.258Z" },
+ { url = "https://files.pythonhosted.org/packages/db/f7/7d5c91bb9620db300c8ddb05337a9014301acb626223faff3abcb8ea47d7/aiohttp-3.14.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c1d60eafd9c7e8e74abd03a5b00df44e7febfe6d9b89b559c0a6551eef0699d4", size = 1843621, upload-time = "2026-10-11T01:02:01.417Z" },
+ { url = "https://files.pythonhosted.org/packages/30/0a/b208953b96d8f24b75f6da704f508e6c5cf3022f52b60c61933df082e89c/aiohttp-3.14.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:137351bf20bbed9a65e839f4a4452ac377389bdb2f2857d2acffef38f5e9f2d1", size = 1927915, upload-time = "2026-10-11T01:02:03.697Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/79/90ebcccb55e2d1e11a1fed581d83bb966e38fb35fb4b2577fdc980f8707a/aiohttp-3.14.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fba47bc2c3d7303c3d027c6cf4d07626c37b1314ac81f5820c31032e0ca1f677", size = 2036339, upload-time = "2026-10-11T01:02:06.046Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/66/55a8904b3a129fafdf94f9cc0a2e4ca09a3c914650be52355db7ad0bbdb6/aiohttp-3.14.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94684b879ac1d71e4238850c99b62dc1b28d9086b156a2555f082010b85a865c", size = 1888041, upload-time = "2026-10-11T01:02:08.384Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b8/96b25da7329a52e42c812b1e8b076386039ec4fc312afa043d173d8147fc/aiohttp-3.14.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56572c42e3ecd636de8d2c3dd54cf5fc939cb5c32eb56297f176a0d366fac622", size = 1703849, upload-time = "2026-10-11T01:02:10.903Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/43/fbf976e3ae4c038d6f5c84945ab2150298c2d71201674d4e53d158063e75/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a95529a92a446db351675f4aab518feaf5e99842f63f5dd17160c2b74f382db3", size = 1849703, upload-time = "2026-10-11T01:02:13.15Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/7d/218e912f4c1d89bde7ac551409be57ad2f6121638e56942d395a7cb1fa58/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:3edbece0379b8b4aaa67619b8aa2399bb66fce372cd5911098a434ea77220aa0", size = 1852695, upload-time = "2026-10-11T01:02:15.295Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/42/252a1b9287e3b6e393a1c3bd1776f36af30f5f25f071bbf2b0cb7eba9116/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:56d9828f204331a5ca8850fcfe2bcce95a149f1f223f60cc7216e5524978e480", size = 1893493, upload-time = "2026-10-11T01:02:17.93Z" },
+ { url = "https://files.pythonhosted.org/packages/78/97/71cae83d5100556fad1521684f7cd1e3e578432644f850245ed3bd969310/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:20064a177a070d789ee64a50b01a9161d3468e989baacfc6c714aa685c4b332f", size = 1693468, upload-time = "2026-10-11T01:02:20.666Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/69/73d88e97a8b5f0ca7a946d0011c0de99fb188b1687c7948ecd0553dc5bf0/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:81c2b3dfd56c62bee6108e4852d5970b4cf9086390b6983f52b666e878c1f115", size = 1915398, upload-time = "2026-10-11T01:02:23.011Z" },
+ { url = "https://files.pythonhosted.org/packages/05/f0/881644bcb15d4b258daea9b720a0af9dc4330496cc8d6ade9090cdd0cffc/aiohttp-3.14.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:09ec102b4b8c9a920275733bbc11fdbb615efe6f9231a06007c0218d336fb77a", size = 1871470, upload-time = "2026-10-11T01:02:25.278Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/de/19d9ebbcce5aedaa3242d8a99ff8816a60bdb7629fb0084bf4a45bfd1f62/aiohttp-3.14.5-cp314-cp314-win32.whl", hash = "sha256:9c428eb2bd8817588d16a0ab898aa4eb5d141f896aa2b394cc79a4cf61d9a8e2", size = 485517, upload-time = "2026-10-11T01:02:27.579Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/74/8cdaf0e58c2588371670d5a9a8215bbb971d36940b6e5d051967dd05c07d/aiohttp-3.14.5-cp314-cp314-win_amd64.whl", hash = "sha256:6f967dde489ca6a8c02d093ab245d2cbf50ccb5c36adf0188b17b0ca39d24b67", size = 511755, upload-time = "2026-10-11T01:02:29.685Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/6b/e0100e25502430a531c7cf1482a378d0b65bf728ab60c01ee270e56bc469/aiohttp-3.14.5-cp314-cp314-win_arm64.whl", hash = "sha256:1d2d981b53dd09a319e3570ef8cc3bbc3ef86f5a7abef0f6b2bff3867db3a9e7", size = 495151, upload-time = "2026-10-11T01:02:32.163Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/c2/ca2ead7b655688c53c03aeeb6e96e6851c9ff08d6be7b13802f53a6ae8fd/aiohttp-3.14.5-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9ce66feae6ac65327379460380549bf1b8df8e17c4e25df2a2bcf168272e3bed", size = 855396, upload-time = "2026-10-11T01:02:34.443Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/70/22206fea409255a240c926ce11de48de354ae2bb90ca44f709c05497585d/aiohttp-3.14.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:27c2322e03f66101acb09869ce1cf1efc04994ee95e1735b69827bf8c8b9d781", size = 567734, upload-time = "2026-10-11T01:02:36.644Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/e5/79a36c118308b56f8667d67e05d2fb6dc638ab45985704cdb199631bedaa/aiohttp-3.14.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff75a7537413a86e7cafe98e0e1d6e3dc4b15c6349896e7d5c6b881bfdb6d550", size = 567379, upload-time = "2026-10-11T01:02:38.757Z" },
+ { url = "https://files.pythonhosted.org/packages/63/a3/2ebec7dece3b1f02c30d2e484647f6f7b13952b1bb40a4cb285b849e8432/aiohttp-3.14.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c061aa954daaf57d2a4b8374f9fca621ef0e1b603584431c220c22458c59b6d", size = 2050686, upload-time = "2026-10-11T01:02:41.169Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/d2/7e4d093db2f4450482652e7ef19a9e19919028f5135aa52bc4078c3beb80/aiohttp-3.14.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1612fa5857b37bf32e5c1eaeefb96e3b01e9c70679eec81f0934e8a600080863", size = 1915077, upload-time = "2026-10-11T01:02:43.563Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/88/bd40d09958442a0a1df67da81de496361d6e2afc04f9db2950d835da750b/aiohttp-3.14.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:adbeee7d6fd4cf5fe0aece2fb3edc4243615d3180430ba8149d01a90670cac99", size = 2027204, upload-time = "2026-10-11T01:02:46.185Z" },
+ { url = "https://files.pythonhosted.org/packages/63/eb/3a601c1f8d3103c1a60ea981f20924855da9f575d2007fc38f8898b792fb/aiohttp-3.14.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b2966998927d7bed9db12c0a4647b0c7b179755878fc9c357fe1ffd3e3b0c1a5", size = 2149520, upload-time = "2026-10-11T01:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d0/4e41bfe1b1ce1cb6f6d2e59fa7a88ef5cf92c402b2d07e9018778ba9edbc/aiohttp-3.14.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e317e0fb6b16212c881d2205a7d87414c29acd69320b3aa6dce9d9c7b86fe4f", size = 2002039, upload-time = "2026-10-11T01:02:51.293Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/5e/72067019545c502b881b031153c437752ecef48d7d213bc0218ccebb4bfb/aiohttp-3.14.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50343c1757b4b6f6708eeaf24534b32f19dfb99fb1b762c00420867a62fc81e0", size = 1786164, upload-time = "2026-10-11T01:02:53.533Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/fe/7741efd6119bfb7a00827fe6f7b84b4409de58d888ae21adc5a9a6824992/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:083673c7a94c3ea035caaa5ca04288bdb44887abfe1f5ba23294e6a4b03efd2d", size = 1970689, upload-time = "2026-10-11T01:02:55.888Z" },
+ { url = "https://files.pythonhosted.org/packages/43/e7/342a13bf67f34d269bf2f7e870ecd72b99c832cc6f0a271a9210c0ebfb84/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2528cb4c6b92008c76ac9ac6298624069bb2db91ff4929905512d1d84485f658", size = 1951967, upload-time = "2026-10-11T01:02:58.467Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/d4/fdb3b27340617e5e64df18a70fa89097778652ea5c7c7e2f79def76999c3/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b1b8ece1e71132d2afba4dbc0c3d62c766e25165990b25db1196c04969eb3d84", size = 1968511, upload-time = "2026-10-11T01:03:00.883Z" },
+ { url = "https://files.pythonhosted.org/packages/83/b2/e8f88298de78d1a951f36f9f966d38ec6ed1d4721303ba02064a545e8aa6/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fce9523df31cea6284f3e2c479876750d7687cf671d7b25d32b19effc0e86441", size = 1778497, upload-time = "2026-10-11T01:03:03.206Z" },
+ { url = "https://files.pythonhosted.org/packages/22/68/9ccdb93d664345c546be7f34480b921774d8c0d98f47e70d7e03b115d475/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:09e0eb18c7e0c8777e2f9149de63799195b9b3ca1b5c81ba6f32f2c6b8628210", size = 1999676, upload-time = "2026-10-11T01:03:05.829Z" },
+ { url = "https://files.pythonhosted.org/packages/57/4a/a33cfa6dcb00e94194ae4fe710432ca4ed111e016356b4ba4d2f4c3a124c/aiohttp-3.14.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6774814fd5c338e72ee0da5cbb9432816df450e69c019f72b5d29bdec2a1792d", size = 1963437, upload-time = "2026-10-11T01:03:08.196Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/20/eacbecfea3b5c3dcbfc9b023e3a5460f43e5dda16d06a2877ebe7184c3f3/aiohttp-3.14.5-cp314-cp314t-win32.whl", hash = "sha256:33f706574e32c6e694f352a856e05caf18f7f2c871b3e87b41c55ea452b409ab", size = 503973, upload-time = "2026-10-11T01:03:10.481Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/78/18eec294f6c8c5dc845dcf6d730a0147d8d0f17e86138a7bdb85e43a30fa/aiohttp-3.14.5-cp314-cp314t-win_amd64.whl", hash = "sha256:5ba14a839fbe87cf7c12a6b5661c05f324a296eb8363141edb3944ba63d4c9d3", size = 531190, upload-time = "2026-10-11T01:03:12.716Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/39/e53f8169acc85271ebd12b5b32ad7f1541b35639ccbe0f49034c64785d10/aiohttp-3.14.5-cp314-cp314t-win_arm64.whl", hash = "sha256:1061b364556e8172e8d46b0b183adeeb73e8c42d30ebc745591e1bd89acad52e", size = 510360, upload-time = "2026-10-11T01:03:15.08Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/1c/06d89f58b2db3ee92dd377217659d587e06f973e57bf0a97a0d8a4586c0c/aiohttp-3.14.5-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:788ecaa9c10533b786ce5ba70c4f2df78ad41819fd00a6c99d92b66f9a32e1da", size = 540674, upload-time = "2026-10-11T01:03:17.483Z" },
+ { url = "https://files.pythonhosted.org/packages/18/39/5e822e038f496f0540ada91e27099d48f9e7f919b6c6deb4cf6db36cc706/aiohttp-3.14.5-cp315-cp315-android_24_x86_64.whl", hash = "sha256:5c76f1802bab718a68ac3cce447160605c734551f95c67ae90fa1132b215cb29", size = 554183, upload-time = "2026-10-11T01:03:19.71Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ff/0cf2619d902b5b160762422a7e5e02091295766a7fe4fcf5b9a655e386c1/aiohttp-3.14.5-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:a6d02b4c38de03d9c7617813433e6a0fb6b522797974177d69d9dad431900833", size = 523160, upload-time = "2026-10-11T01:03:22.193Z" },
+ { url = "https://files.pythonhosted.org/packages/86/99/3553abfc53a40849dbaacc3f54c730ec58410809ffa2d05885ae56108f2d/aiohttp-3.14.5-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:20726f9782d5c2744c1c66255842d1d163bb3edcf768b8de25216bf47f7b6ccf", size = 530311, upload-time = "2026-10-11T01:03:24.435Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/a4/5d25f73754bc1e8f983ba704e86d641aea290c967f195f2aeebdaed2bd84/aiohttp-3.14.5-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:248d779ad720b49d4fb355720e60c9e5f444f95887bc16974fea48fc56c41789", size = 540274, upload-time = "2026-10-11T01:03:26.698Z" },
+ { url = "https://files.pythonhosted.org/packages/29/a4/07eda5db2e3ee017d9590f36c12a94ec6f1f50516e8df78672373dfc7185/aiohttp-3.14.5-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:0a8ea271867e360ac985ae607f4a23ad9a38414b9aca1d49ec98839ae660e49f", size = 819239, upload-time = "2026-10-11T01:03:29.481Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/aa/a8723dd987a696dd48d4cf2f0088e589ce77caebff0b96f2a78c20424380/aiohttp-3.14.5-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:823c910f046f23f4c713b8d99a2242dc65f591cb45ee86418fa11762a3c2963c", size = 549832, upload-time = "2026-10-11T01:03:32.02Z" },
+ { url = "https://files.pythonhosted.org/packages/29/5c/969a1b72692055fd2a419590c41847ec9144fefb97b75ff1cde5b6372891/aiohttp-3.14.5-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9b42db919715e91eb76acf3bc492a9a7ccd8bd9adc6745c1412b689735269f14", size = 549234, upload-time = "2026-10-11T01:03:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/38/05/8e3e07fd8a0d33d06955ff4e54a1cb92f4bce347ff55e441dc3e25a7b5e5/aiohttp-3.14.5-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3112585250b199296c26ca6e0131640b6a8d01bab8b232d2eb3763ed469de11", size = 1891314, upload-time = "2026-10-11T01:03:36.899Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b3/05a79ce2e25f024e93de30c94f39dc6aa6e2bc1e9c531a5c4b18dc61b7a4/aiohttp-3.14.5-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c147451b4a58e7050f7f7394e6c467867c84161560001f9ad4fb2d1446743946", size = 1884372, upload-time = "2026-10-11T01:03:39.334Z" },
+ { url = "https://files.pythonhosted.org/packages/94/52/0fd8af0717db109eea258191b326b5cb5847fb88928bfa6c862fd78b9ad3/aiohttp-3.14.5-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bf163cc701f3d4ac43ba7d97771bf5fd955220ef5500ef3ee847bc0ecfbf4ec1", size = 1930957, upload-time = "2026-10-11T01:03:42.172Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/b3/fa78733da88812bf9fb193913fb0ce1548f8b6912047633fdf88a758ff8c/aiohttp-3.14.5-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d40ce41991e9d56fab4f5dc4a51fe59bc3b5c77c27f4b148963064d00232e8", size = 2038886, upload-time = "2026-10-11T01:03:44.646Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/f5/2fcc5e30053a938286f17d0edf3f0850b8061b984256fa7c26850b9c8809/aiohttp-3.14.5-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:276a4fc00b1d9ae492b802763a789c5b86328b989c5ea169f2faa447d6a11c7c", size = 1894470, upload-time = "2026-10-11T01:03:47.304Z" },
+ { url = "https://files.pythonhosted.org/packages/62/2a/f87feb42abe8e6a7c03814dbcb711540849a1e90aa392055f3183c643610/aiohttp-3.14.5-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50983e3be33d8c0942ab88cec3905b10602f64c469b20153c48c5d4e558dd016", size = 1716273, upload-time = "2026-10-11T01:03:50.121Z" },
+ { url = "https://files.pythonhosted.org/packages/81/b2/adf1f960dd977722ed1347d33da512a1624807114f57a3b91f5cc828e081/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:16c8abd5bca220a47efe667d26f8460124c81810787e79ee87b242677563d9dd", size = 1857513, upload-time = "2026-10-11T01:03:52.959Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/fd/ef8d910e641de4160026a513ace5888b7f92826bbc3fd90ced05d55a828e/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:0790ec66fa4013e83c53b9025a45d454723da1a2fce28b3208c9b32d08af162f", size = 1887704, upload-time = "2026-10-11T01:03:55.641Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/db/6c9142f941cba8d35be8e1fae6ea2bd390e754fc14076b8147aee1a4592f/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:cb11a971a3aea10f9b8373be628f1df932964fc6c6b174516d318a48c3ac4412", size = 1895684, upload-time = "2026-10-11T01:03:58.18Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/7c/6a6bd9a72e576d376c668333b00c51c6147aba4fb863ed6c94996d497eca/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:932ce7e694bbc29b2bf6f64f2343c27d148d4997c771d01bdade4639b6749ff4", size = 1706299, upload-time = "2026-10-11T01:04:00.8Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ec/d2cc494242f8c4d3d1cd70baf591742818a74386dc3b84af3195c5c4fced/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:a7d470cf7b206e6359fc77b1b860632fde400d5a2ed59cd0181b93a686bc81ee", size = 1916645, upload-time = "2026-10-11T01:04:03.892Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/6e/e852c53647e815db09a1b6b5ab634f54bd736412397e11940feef4d2c89a/aiohttp-3.14.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:6e1d8637cf73eebc92eba2e11d4cfff98a3b562f2505bd75bba766d908926e8d", size = 1875408, upload-time = "2026-10-11T01:04:06.922Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/f6/72ab6ef20c332399be593bac543d2c24a6d241e39d3540ce9f95a63e4bd2/aiohttp-3.14.5-cp315-cp315-win32.whl", hash = "sha256:fbdc5ec49f9ca3cd24955cf3520b10a4d4c901ba2572094c84274e9e7eb30534", size = 485590, upload-time = "2026-10-11T01:04:09.626Z" },
+ { url = "https://files.pythonhosted.org/packages/06/eb/e9de75b8c6d2170c42c08ff303abf857ea8a6d9d9b6e99b5aba40f15e962/aiohttp-3.14.5-cp315-cp315-win_amd64.whl", hash = "sha256:a9d3983bd6ab7aa1cfd573544ae98df9b6cb6912a5185a198263e024a636861d", size = 511740, upload-time = "2026-10-11T01:04:12.277Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/25/455f3c2785eb0d50748cffd0abd07500815f419a9495b14610b7622d8d2d/aiohttp-3.14.5-cp315-cp315-win_arm64.whl", hash = "sha256:e29347c142cf6e99e0dff5e2995ead1d50fa3b51bf37a7c726a7ccfe5419745a", size = 495171, upload-time = "2026-10-11T01:04:14.667Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/6c/497f0a98782eebfcf0f02a7fbdef5428148bd426027140cbad494cf842b5/aiohttp-3.14.5-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:9bf1d5dcc15204d9ec8b8ea4c18fd66e6b80e5de1f4ecbafb3a2f2740f8039d4", size = 855638, upload-time = "2026-10-11T01:04:17.154Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/c5/55c0cef2572af9b1ee81608f7c0a1bf74e6c9151a73b04915d933f192335/aiohttp-3.14.5-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:14f04769cfefe4734016a856a83af36133cd17779cef9ae817f812b8ba9d6d51", size = 567455, upload-time = "2026-10-11T01:04:19.702Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/47/e1a0e39f4a2b881f6071225afaf94a6547001b5aefc1566734d73c685934/aiohttp-3.14.5-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:6e4251c0ba4624a68a2c11471a1ac54c3306876c21f0ae86de085cc9241c8905", size = 567836, upload-time = "2026-10-11T01:04:22.235Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/1d/817d85836f52b687160064a326e62e037dc42f1ad5b6b5188a70e5c134b5/aiohttp-3.14.5-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4f5cf4dc72a71c4cfa9751b4950be22f733626670230d46e7d606592aa22d59", size = 2051409, upload-time = "2026-10-11T01:04:24.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/25/e8ea6fc212a9aabce982917346ce8ecab0929c74b60232a056dd11c99da0/aiohttp-3.14.5-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dbf53ae2601b7fd5a93c3944deea3a78d40f495226d582c35ef7a433425ce2b2", size = 1936493, upload-time = "2026-10-11T01:04:27.456Z" },
+ { url = "https://files.pythonhosted.org/packages/24/33/de0517f71f1a19feea4aff78a2ec4ec2d98634129ec30ead78fce2f8f81b/aiohttp-3.14.5-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:657291433bf4dd3142f3abac495764cd47d0c7c92087751e6666c6447e65fcef", size = 2021405, upload-time = "2026-10-11T01:04:30.276Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/11/ddaf2e7930543e9f0cad3a1c54e1af0c1bd2fae3973d568c4263d3b010e9/aiohttp-3.14.5-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e51a27980c3788e6e6b3325d694fdd4898087fa8a86b2763af77b39353da41e", size = 2142017, upload-time = "2026-10-11T01:04:33.022Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/7b/58784353c06de8adc20f426daad3d85dd86fd55331c713a3f4b638c94573/aiohttp-3.14.5-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82c7583cd3dfdc7dcc927835b4f6c7faae7ecc1ba3ca5879321621ae2e6f8e84", size = 2002482, upload-time = "2026-10-11T01:04:35.94Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/f0/417d9535caa9e165ffe6a53e2347a78107e7c87dcb0e10aca315c3af0344/aiohttp-3.14.5-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6fdcd6af7e2e51d1ba1b4bea16e97b074bcb7b5dd0246a9d8201341bb28085a0", size = 1791774, upload-time = "2026-10-11T01:04:38.733Z" },
+ { url = "https://files.pythonhosted.org/packages/98/01/25e49c2e8a01b9f0e19ca0a8448ad50aa2bdf96c8cd41e92bd45af044784/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:c8859a013ae0de1074660992139a1a440df3e6b219b86cf0d3f11c2692bb4fe3", size = 1970358, upload-time = "2026-10-11T01:04:41.66Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/fc/c132fd3465b6c7e4ce0193154f602c3e6c46b680e4da7eb3bd0d8a40d1c7/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:8966ecac808dd5f473c9c4cefd10cd3ffda71c18a4d3493b7c7d2ae1803bf2cc", size = 1969192, upload-time = "2026-10-11T01:04:44.66Z" },
+ { url = "https://files.pythonhosted.org/packages/95/4e/d58b45e7dba4eb607eca11fc0a4aa77ae0f39c11afa804635a61ce18cff4/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:3093b72c215bda16ce961a6d073f6e71d46e022962a9d5d457c5d4d421c78b57", size = 1963618, upload-time = "2026-10-11T01:04:47.505Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/d9/f6ac50946efb3490428ef52b56e62c1c6b7f9c6ff3ec6083535526c83e60/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:149fb56caf7acb67073126f675d0958d9c4b3125fcd3f6d4877df98aa8a97ce9", size = 1784817, upload-time = "2026-10-11T01:04:50.287Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/2f/f255eb63da788cd8a452fe350869c03266ccad7d884925f6963c87808f24/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:293d3ae7c6a0ed176a42e59a1b5fde825ead65c835360f734148e96729f928d2", size = 1995956, upload-time = "2026-10-11T01:04:53.213Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/9b/241aa3393eaafda0034470add1625f82a4c252901108723b283a9b0b32ca/aiohttp-3.14.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:3f2dcc00191fd563e9075181a14ec31d7dd63223ced7582cc70a15a499de0c79", size = 1967794, upload-time = "2026-10-11T01:04:55.958Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/7f/a68e689288c9e4bfcf8d0979f2b12b774bf01861985420df6150eba5448e/aiohttp-3.14.5-cp315-cp315t-win32.whl", hash = "sha256:7779cd97e61ebe583ec2f1c5616cdd038aa08a4453b1848c67842176d054948e", size = 503876, upload-time = "2026-10-11T01:04:58.978Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/78/49b0299da6d54de19fc6fdc6889d50233ac47d192a461624f4fc010fde83/aiohttp-3.14.5-cp315-cp315t-win_amd64.whl", hash = "sha256:0e6f16f5e49c4b8267988c05ab07760d7064cea57d077c3d068d04b0fbb992cb", size = 530957, upload-time = "2026-10-11T01:05:02.23Z" },
+ { url = "https://files.pythonhosted.org/packages/21/d4/b0afc936aeb6d2f93157e3408b069ec5d7934429ae7d023de5e0953b1887/aiohttp-3.14.5-cp315-cp315t-win_arm64.whl", hash = "sha256:1aead151c3abbac6b32942e452020cb66d7efc099d253cc6c20f748e926c858b", size = 510408, upload-time = "2026-10-11T01:05:05.362Z" },
+ { url = "https://files.pythonhosted.org/packages/68/30/173960c42b05a6c59f7558e4b12a4b0d9ba376cf6aa9bde7f9e08a30ca8d/aiohttp-3.14.5-py3-none-any.whl", hash = "sha256:efc21a454892828368b11c2c780de0ff8bc991f73f6b99c6b66e56205470929b", size = 279517, upload-time = "2026-10-11T01:05:08.523Z" },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
+]
+
+[[package]]
+name = "annotated-doc"
+version = "0.0.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.15.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.15'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "26.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
+]
[[package]]
name = "backports-asyncio-runner"
version = "1.2.0"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/backports-asyncio-runner/1.2.0/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162" }
+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 = "boto3"
+version = "1.43.116"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore" },
+ { name = "jmespath" },
+ { name = "s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/20/de5848b0166d8cb6b6e597b299ca10a64ebba3cf9aaab228de1e817ae85e/boto3-1.43.116.tar.gz", hash = "sha256:89fa15e7d1788fb17cbd694b034beba57f43f30f58f4506913a5b62220f8ba01", size = 112703, upload-time = "2026-10-20T19:34:57.874Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/de/cf71466f55516e61d8048374776b390a036a1b48c9a7a4cd8e21f8b27980/boto3-1.43.116-py3-none-any.whl", hash = "sha256:9dca4dbd464b189041b27b5a02280bd5e02c3051bebf6bd03cd0e47aaf558030", size = 140042, upload-time = "2026-10-20T19:34:55.932Z" },
+]
+
+[[package]]
+name = "botocore"
+version = "1.43.116"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jmespath" },
+ { name = "python-dateutil" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/93/98/aff05e8a35f5bb9f6648c6056ecff02a2bd4540ad70a89215db8a0cae758/botocore-1.43.116.tar.gz", hash = "sha256:00b1fbfc1c024b56633538385628a1bbc7ab15aca0b5b53e5fe188841d96c8ce", size = 16411854, upload-time = "2026-10-20T19:34:52.578Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/2e/449851ffdd5c6cd521d309bba501077025929ba0ef005a3052f43fca509e/botocore-1.43.116-py3-none-any.whl", hash = "sha256:92f1efe8434183f08f91a61478925f5f8d23082024556f269f916477040e26f4", size = 16111072, upload-time = "2026-10-20T19:34:49.566Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2026.10.19"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598, upload-time = "2026-10-20T02:24:16.604Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464, upload-time = "2026-10-20T02:24:15.072Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" },
+ { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" },
+ { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" },
+ { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" },
+ { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" },
+ { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" },
+ { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" },
+ { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" },
+ { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" },
+ { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" },
+ { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" },
+ { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" },
+ { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" },
+ { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" },
+ { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" },
+ { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
+ { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
+ { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
+ { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
+ { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
+ { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
+ { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
+ { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
+ { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
+ { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
+ { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" },
+ { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" },
+ { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" },
+ { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" },
+ { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" },
+ { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" },
+ { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" },
+ { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" },
+ { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" },
+ { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" },
+ { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/33/1c/f41d4e74c28ab327ff3acd36053f7ea506c55872d7a90b0fa71aa3ab0c89/charset_normalizer-3.5.2.tar.gz", hash = "sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef", size = 172659, upload-time = "2026-09-30T04:39:23.398Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/backports-asyncio-runner/1.2.0/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5" },
+ { url = "https://files.pythonhosted.org/packages/98/77/46e87bcfc45d25ab4db7cfc9bb544bfa3ffd302289ed31ae93f5433eb899/charset_normalizer-3.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:195c26fb65950f8fce54e26349852b7bdd7c5f120aeefbcc440b8a20faaed4a3", size = 373348, upload-time = "2026-09-30T04:34:42.843Z" },
+ { url = "https://files.pythonhosted.org/packages/52/fc/e518013affcc43c9f919c3ba41bffe9b4ee4aceb0a6462a6243efcca5f2d/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9373ad13ef0d2c0fb761e04e55bfdee5a08b52cef2c882c8fbe9935b1517152e", size = 260860, upload-time = "2026-09-30T04:34:44.817Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ad/2a895c945ee61988dfd9ccee64f0b78dc09f29c9b34d1dd545246d78e0ad/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ddf19c062bea7a0cc80f519243d2c01dd091be0cf952a0750d4ad576709559f5", size = 246491, upload-time = "2026-09-30T04:34:46.227Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/05/5d958bc8ea503e26be25ada5430fd409cfb45dc22ff33f9f96e649613c99/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d14b50de6bf4d0edf857a9386836846f982b8f524e188e2e68b96d702bcf4aa", size = 290875, upload-time = "2026-09-30T04:34:47.691Z" },
+ { url = "https://files.pythonhosted.org/packages/55/e2/06bad57dfdb49cad92c0ba85b6b4fa5827a67df37897287cfef0553843c2/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:28a15fdad492a99b6eccfaaed66ef3f74050680545ea61ec8b2f4c538f1f1320", size = 286987, upload-time = "2026-09-30T04:34:49.356Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a893cc101149f80a653f82062ebc95b34525a2614382e1da5458fe7c6997249", size = 271105, upload-time = "2026-09-30T04:34:50.957Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/41/e05e19578b7b87e7db2c3ecb884bd09d065274ab41e1f535542e8bae9b06/charset_normalizer-3.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:619799369eeef6366ed3e8755a5670f4f2f0fb6b30a0fd7264dc0fdc2357058e", size = 269833, upload-time = "2026-09-30T04:34:52.623Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/cd/fc7152414561ff78f9c61a4a627025f65fd244abaefc99ac4e75c6169b33/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:447441e76ec720b15e64418d32e092297340387053047c7c694f579efb0ee1d9", size = 263048, upload-time = "2026-09-30T04:34:54.013Z" },
+ { url = "https://files.pythonhosted.org/packages/71/71/fb379e399b1013962716a059d551e03bd80b3e02f05d81246886af6c0958/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:62588a277bfb59def052abd940703fa35107152bf479781a878617d60faf8fb5", size = 249237, upload-time = "2026-09-30T04:34:55.373Z" },
+ { url = "https://files.pythonhosted.org/packages/33/4f/aeadccd6d20882909eb2597ec40eccc05d505e2262f14dc76d7620700657/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:44bd4fbb29dfbeba60e7d2bd000c59e4b21ddb3cc53912b14048d37092706d7c", size = 290756, upload-time = "2026-09-30T04:34:56.867Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/7b/e8a92613236b257d3b75d532496eb21f68ad8231d7d56df94497e460112e/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:30fcd120b732aa79317f08dee04d7de0847822e4cf7ee0e9f445bb958832252c", size = 269026, upload-time = "2026-09-30T04:34:58.278Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/ac/21d5c6b972285c5f095ff78afdc99f3539e3985dfcf7ff1cfbe9e772f529/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:50e3adfb96fc189eb27b1cf62d3b598b89b4bb0420d93a3d3e42e137409011be", size = 287601, upload-time = "2026-09-30T04:34:59.703Z" },
+ { url = "https://files.pythonhosted.org/packages/59/72/263491ec1494a194b16fcbff88a0220f2af633738c79e92b4d7189226322/charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b736353c0a625bbd5fcec108576e2385db3496f4f771f785ff32e108d3c3bc45", size = 272660, upload-time = "2026-09-30T04:35:01.26Z" },
+ { url = "https://files.pythonhosted.org/packages/38/7a/81f1186c57e406233969ae96b5fd0c907254ab8b6e47da8ae24cd20fb9d7/charset_normalizer-3.5.2-cp310-cp310-win32.whl", hash = "sha256:f5833ad231be5eb6553de524a70f48d71b2c8563101750531e0b80184e175cd4", size = 187301, upload-time = "2026-09-30T04:35:02.659Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/ab/fb8b5178fdf7bf68efa61cbe892286bc9f73846f26147d4725fb89388475/charset_normalizer-3.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:1461ac396c4fdb983a675f20aa555624f0ee18ac83d832b9244ffff3d8055275", size = 215446, upload-time = "2026-09-30T04:35:04.011Z" },
+ { url = "https://files.pythonhosted.org/packages/36/40/e270b74e0d3f583d78d902960845a149e80eb7c737ee5613ccc6efc7264b/charset_normalizer-3.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:c6708715abcf3c73b99508253e961a9967f02fe536532834149574eda6de0d1c", size = 203389, upload-time = "2026-09-30T04:35:05.577Z" },
+ { url = "https://files.pythonhosted.org/packages/22/67/6a0b94a7960d5e1b5eacd2fb529f3fccc47db4644f7f0a7cfdcfc3be578a/charset_normalizer-3.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d21b8b13c7592db2ac5e544a6d83187b995257472b0c9e8351b6d507ae37ed6", size = 370642, upload-time = "2026-09-30T04:35:06.91Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/94/01009e13b94041599004edf32e56e382c24e570f60f79bab8efe45cfe1eb/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d760fe2a4d7c3b226cb9026d6a842868d52a7901bd98420e1baf14e80da85cf5", size = 259280, upload-time = "2026-09-30T04:35:08.448Z" },
+ { url = "https://files.pythonhosted.org/packages/66/85/3b5358f60a13210f0b67d3755c168ef758701b021e655d88d4da28554467/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9790464842f85f437dbbb54417eda1e0e6bfc52dd8d22d6fd1c994b73b2dc74", size = 245335, upload-time = "2026-09-30T04:35:10.104Z" },
+ { url = "https://files.pythonhosted.org/packages/74/75/77c1c479b09ecd751d1e767b251ea5c14d4d50ff757bf404afab2692f600/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4685902cf26edf013ed7a3da0f426ebba7a00ebb9541386d835afbf002c11cab", size = 291707, upload-time = "2026-09-30T04:35:11.575Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/0d/363f78cacb70f58f15f4b083961bbd9d292f335d3f5c66fc4f1cfe69cb90/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4495c5002a7b28557e7e222e77e0b661183e432b7d6d2e788101e3f240e05b8c", size = 286747, upload-time = "2026-09-30T04:35:13.022Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/ed/cf505d3011ffceb12c2067a7a5d3cfe92b875d4d44bb0ff0d69375e2c184/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:211d5a3eb6af8f513b8d4ca19a8c1b7accab1b5f0d3175f9826b03c1a920dc1f", size = 269972, upload-time = "2026-09-30T04:35:14.606Z" },
+ { url = "https://files.pythonhosted.org/packages/15/d8/f0a93a431d170e7ca681d4f6650fee3de934d18560e474e7267eb4b0f987/charset_normalizer-3.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef4fcbf3327382cd4c9f540babd61248208af7b93eec4de397b4d5f58a09e288", size = 269339, upload-time = "2026-09-30T04:35:16.087Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bd/9b2bd1c5b7af02462c9752d33994834ff972a96b4c483eefde9e594488e2/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd16aabe4a02a297c23417aa17ac6299dbd8c49f673bcd645b4929b11f5a4400", size = 261683, upload-time = "2026-09-30T04:35:17.488Z" },
+ { url = "https://files.pythonhosted.org/packages/76/a5/cac540ab0fd61f3fec88ad3dbb64509e71424593d73cfdfff5ab3e4db279/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fb9e68df06293761f9fe66ade60a9bc6d0f5e42b8acf2939a9158af86ab0e5bd", size = 248143, upload-time = "2026-09-30T04:35:18.849Z" },
+ { url = "https://files.pythonhosted.org/packages/71/7a/ff467301deef2089fad87f72df9e000a26a78fec7acbb18e1999371b8369/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:59f63901b0031c3136cf64704dcb21de0bbae62ce2c9529bc39d27665463de37", size = 292358, upload-time = "2026-09-30T04:35:20.326Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/77/22d7e785d1e210afc2e2f58600dd1799d17a35665faf84383f002826c5f8/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:304d5463e65a35d7bb0850550e0780395395f6fcf452f04db7d5ca7cecc425ac", size = 268766, upload-time = "2026-09-30T04:35:21.72Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/91/e8e946267f1c2d9e2bd651726e2fbd2addf02c4d36cea5069e32ca9d7bb5/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9cf9b1a857e25c4baceeb3624e92a56df3668f398c4acba74e174d81fb4d1d3a", size = 287432, upload-time = "2026-09-30T04:35:23.273Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/88/7561d8a88d555e7df6623abe7c0070b4baf47549b9408783a2ae0a1a6cf7/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:114e4d0c92d618409ed82a99e22b5c5e768fe995f2973f78265f4524f49d4640", size = 272470, upload-time = "2026-09-30T04:35:24.655Z" },
+ { url = "https://files.pythonhosted.org/packages/35/7e/578c702301ec036f01455f30744a08d2b42f6ab35b9b2d4bf8cae0ef2a80/charset_normalizer-3.5.2-cp311-cp311-win32.whl", hash = "sha256:2625388c6c754520c37abaf3b41eb34d1cc4a373f457898f08606c8e362b891d", size = 186688, upload-time = "2026-09-30T04:35:26.225Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/fc/fdf8cf52ff21cd5bf158f20978991cf985325842f74283eb6df26c8a39d8/charset_normalizer-3.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:87e50a3e7cb90af586b6c5faf23e302a970415ac73bd7bd90a515a04b427ef96", size = 214932, upload-time = "2026-09-30T04:35:27.796Z" },
+ { url = "https://files.pythonhosted.org/packages/97/66/3e45a506d8110b632541faf9a9470185aa9878f1ed44020f31346c1c5e5b/charset_normalizer-3.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:254eb48b9fa5ee9898a3c445825a1f340fe53712a098904b39b0bddba8ea3cb1", size = 202828, upload-time = "2026-09-30T04:35:29.259Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/c8/693809898870237d82785a03f3b2b58fe4c9f14669f84a7d4e623c92a59e/charset_normalizer-3.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed2a239c0ea213acc1908150a3037257083c7c083128f1a4cec2ec4b97dca491", size = 367780, upload-time = "2026-09-30T04:35:30.888Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/87/2fea8c13dc24b3ca9c6f803a5b2dfdeae73eb4f9e12c7885ed908ff0433c/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b91363207bd9dc966a691e959bb47f64b30f7ac4b072be9968b366982f7db77c", size = 246730, upload-time = "2026-09-30T04:35:32.286Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9e/09efac30b937722f46d3110ba30b875b24b2e3a266ed746cc4e376a94d80/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:38a873987f3be698494da8b2e3085e29da02da7b633dce73e79c699a113d7bf0", size = 237707, upload-time = "2026-09-30T04:35:33.709Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/18/70d76670b13686237863a379928d60bd10e021f17d243ab3d7014c4a5f4e/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:355ad8011081dec5412240c087a9a0c9d4d5039f3ed11a3f13e18c2b29b56c51", size = 273050, upload-time = "2026-09-30T04:35:35.138Z" },
+ { url = "https://files.pythonhosted.org/packages/54/e2/77a8b09d5adc013ed07b95b01b8b8fa5441c4e810e83ee7e4aae2fa4d91a/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ee21e28f0430bd6dc9086c6e525d5e818a44a5ad19720c8a0ef766792f3eb5e5", size = 270345, upload-time = "2026-09-30T04:35:36.502Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/c5/38806a25ab5e65fc178f39affeda20858efafede2fce1ffc2556cfc9fe73/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d31298449090ab8d47b7b1b2a555ff73cac7ed438a08b7ac160980c7ebed649", size = 257601, upload-time = "2026-09-30T04:35:37.919Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8d/213565184708fdb263ae55e2c04ee1ff748129dd65d48ed0e3502da9c85a/charset_normalizer-3.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5cde776b7cc66e4f6c99612cea4aa7269aa65863f7a15841b2c264f103822f4e", size = 252222, upload-time = "2026-09-30T04:35:39.544Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/24/76d2cefc25472531e4c5c7dfff68865eb1c39b78482f0fdc15b46f047830/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae4f5fea5b8b8ccff88238cc8569303e5ee95efae67fa62922a311397a71f346", size = 248482, upload-time = "2026-09-30T04:35:41.088Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/dc/65a801b66ab4c197e22c433ab25e7ac24324ac6f45a2269aca42cce309bf/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f7d486c83842422badd511868fd8a9a20e9407ace71564b6af47ce7e60a336c1", size = 241206, upload-time = "2026-09-30T04:35:42.59Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/95/ca9b5eabde673002c6f1e7ada1b223916fe18f6d661da7aabd4d643718f1/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11a4d68a6ecda3292cb1e50239e111543ba5d709bb62a6b4ea1afcfa729d8875", size = 273190, upload-time = "2026-09-30T04:35:44.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/8b/803b4d2a3f6e1740f63f1e87b04d14b42f3d4fdfe6ed7d4db2d34102b14f/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d6734d2ef8a50fbf8445c139477da401f50d62a0606bf00e20ec6d87773fefb1", size = 253527, upload-time = "2026-09-30T04:35:45.915Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/55/93c0e5dbd085ae0471346026abbe7e0db9ea2d6fea74e51f0b5a46f233a7/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a815775b6c38d4e0ff7bcffbeba67feded90202bb6a226b8dd35f1c855217413", size = 271285, upload-time = "2026-09-30T04:35:47.49Z" },
+ { url = "https://files.pythonhosted.org/packages/95/69/0dbd0e0b9b16cfa816cdfcb3e2e3854a1f680dc07fb1245ea125e7448060/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23851fb4e1b85ed3f6c2a27b777cdfe2e19fb5b38429a8faf38c7542b7665869", size = 260010, upload-time = "2026-09-30T04:35:48.996Z" },
+ { url = "https://files.pythonhosted.org/packages/58/9d/e7b88e7b1bf403590c3b573277b5e1e488c68c7a6fbacca310a2c324e90c/charset_normalizer-3.5.2-cp312-cp312-win32.whl", hash = "sha256:db19d07e2e0129e974a0e65d0064fc222a446cd5122c2fd4184d2af9fc734a9e", size = 184125, upload-time = "2026-09-30T04:35:50.777Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/e6/e6e083884cbcfd49c64865af05027fe7011be7b2d9179524f099a1b611f3/charset_normalizer-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:780fbe7cab297b81dad9fb8dc5eb003c0468ffb0d9e5f65068c53a34661a96bc", size = 207486, upload-time = "2026-09-30T04:35:52.194Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/e3/017aea0911ada7405a825c7d937eb3a13009664e2f5b38e8c4bbf2abf894/charset_normalizer-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:e2af3aad578aa6bd1384bcf4750fc285e5a9de53f40b7d41e5a0bf748edeb2b3", size = 196734, upload-time = "2026-09-30T04:35:53.636Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/34/68292d68512768591aaff07c59bb53ee31341c87759433a859c4641a50c2/charset_normalizer-3.5.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5", size = 225946, upload-time = "2026-09-30T04:35:55.313Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/80/bee0b01b90ccd5322ae1d0abb33fab1bd95b7c2eadaf02aeccf22e04ee83/charset_normalizer-3.5.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e", size = 238619, upload-time = "2026-09-30T04:35:56.863Z" },
+ { url = "https://files.pythonhosted.org/packages/78/6e/60ce52a85a7fd631ae8482ae6d74521014ca2f255892679484dc04d7ef56/charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2374b62878abb00cd8309b32af6c0b715cd02dec0ca74ef12e5069bdc64144a", size = 206701, upload-time = "2026-09-30T04:35:58.639Z" },
+ { url = "https://files.pythonhosted.org/packages/36/8c/71aafad23f971afc84c2b295bc0c560739ce1dac558aad9fec22e39f3639/charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d376bbd28b3a8999db1a103b3b388aee6f1ddeb3e51bc2172993efdcd86e064d", size = 210506, upload-time = "2026-09-30T04:36:00.147Z" },
+ { url = "https://files.pythonhosted.org/packages/91/da/3c5a7798c046df7d2d68ad653cf5b6c5a8bfee225055a843c6f2f42aac1a/charset_normalizer-3.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6045373d5a89a5ec71afde535db987ca28e76dfa276c2d4c818265b375d4b055", size = 366626, upload-time = "2026-09-30T04:36:01.77Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/16/710ac3de2ee354e2bd1a9c94efe45a2d27b5c6ad39b2d6a905be2c094b6c/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:849df64e889b2e17230d58410a03dba311a65b163508fd33679b2b737d4b7858", size = 244624, upload-time = "2026-09-30T04:36:03.389Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/39/45c7439f5b63d24f7d5b2a1d760f34af7628782d7144b4cc8ded45c2d4bc/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15c44f7edfd477b06f517a5cc317fc1707edb9de2c865f43d4b6513907473234", size = 235731, upload-time = "2026-09-30T04:36:04.987Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/34/38f3154785ce92e9f56eb226f4d35bdfae6b008480dd055f58837a89c810/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a89012d6d5476ee112d20d998570ed58df2260a852afb1758809cd6900411d21", size = 270352, upload-time = "2026-09-30T04:36:06.412Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f3/859f74e7babc977705026b30593b3be04049632a522fb7000f83c033d747/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0c951d5e6dd9c2ff60609476752bee49da4206adde960ebc247766937f72e718", size = 267393, upload-time = "2026-09-30T04:36:07.865Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/85/41d27f234b82e47c167a5f6c0f62501dc0c640585ff4aba79e08a390336a/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7218e8f32b0956cfcd048fd42d9d5779809745ca1d86113ca56f66e7ae1549c4", size = 254660, upload-time = "2026-09-30T04:36:09.248Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ca/5d1a997587febe5b26d8daffe363b5c1a091cece19828eec6502fd09c5ef/charset_normalizer-3.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19a731138fc27d5682277d3b9df22855cea1239bce7fcec5f78f42ef2d1f3c3", size = 249996, upload-time = "2026-09-30T04:36:10.73Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/1f/d1e78246f7ed60c8c8d606b4ac27f66ce49cc3e95f24893ccbeba9f77302/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62603db9a7caa0802eaa28c1c46fecd7b3a263a774069c24c3c28c302448721c", size = 247028, upload-time = "2026-09-30T04:36:12.294Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/37/eba316edd4f0c4d3a5d945924c4eeeae59abac4056aa815d8a4268f863a2/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b6856554c4f44d79fc2307d5768854310a8f0096e501c75637542c82292b0429", size = 240562, upload-time = "2026-09-30T04:36:13.887Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/8e/aaa037d40ca9ef045977f1a661048b1aa33f223adfce3452fe9be9f79d14/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1bc0baf5ef96b6ede57d47f4b8fe4d9d84019c3bfcbeb20a41edc6a6ee341f1f", size = 271699, upload-time = "2026-09-30T04:36:15.41Z" },
+ { url = "https://files.pythonhosted.org/packages/26/19/1c1c9f75974adf523b87f34b8a2adc5a435cd65916812bcbd0dfa45f9a29/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:56bc200a365efb37383b7852e4cc5898d3b2da5987289b543956cf8cad71018a", size = 252030, upload-time = "2026-09-30T04:36:16.839Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/90/0660ef18e18df0a4d2a1a0edff7dfbba42d4e50ef2425557a5bb7051f77b/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2c9ad19a6cfcd5ea5c0d41161d22f9df1dcc277e9bef2751391334546a314c00", size = 268565, upload-time = "2026-09-30T04:36:18.468Z" },
+ { url = "https://files.pythonhosted.org/packages/79/ba/57adc269824e8658f1a0f97a9e514c247445a9632b3419b97e0ba37f16dc/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e243bd13217235fc7290c621941c3f5cc8b66e4872495be821d7436ba2fb838d", size = 257627, upload-time = "2026-09-30T04:36:19.938Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/85/33abd4315c052d3d4f54c92b1ee49bfbc0dc7115a981e462a793b6d2ab87/charset_normalizer-3.5.2-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:a090bb2c68df85450502e3e20d665e3a5af9c65a84d6508ed477badd49166fd3", size = 143407, upload-time = "2026-09-30T04:36:21.376Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/de/6435e18d1aaa5d910b896d551411c96af1f42a0c56c29afc2016c61ccc2e/charset_normalizer-3.5.2-cp313-cp313-win32.whl", hash = "sha256:2b7b3bbfb4fe8ef40600792d762fbaa9057559f9d3fad209525b7a22b99e91fd", size = 183207, upload-time = "2026-09-30T04:36:22.776Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/76/b8ec57f4e9ee3253541abf95e4a462c0175fe8032dcd070f1f2421240942/charset_normalizer-3.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:78456a747de8dc58360ffa581f30a002baf5aa28cb262536545e91f113ed7639", size = 206489, upload-time = "2026-09-30T04:36:24.306Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/60/c647c6ae47480221e875ea5d743ff94946f7416e3c69415ab772928e8d32/charset_normalizer-3.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:11912e4bb14baae7c5d8791aa55ba0a3a03ec6729073307b0f57270abaa713d3", size = 195501, upload-time = "2026-09-30T04:36:25.846Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ca/7aa91362a2f77ac8e9e28a9b902a74f7d0e11a851ef0d27a74308da8cd90/charset_normalizer-3.5.2-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:1afb975bd5d68d5ce9f6b6d44fdf2f7e34b895a35e95708a7a91b20a3b51d187", size = 226010, upload-time = "2026-09-30T04:36:27.669Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/cf/ac8878d0322cf88a1aad4c7b147db32ca0bd806eb0060957b2e31486dbe6/charset_normalizer-3.5.2-cp314-cp314-android_24_x86_64.whl", hash = "sha256:bbbfc8e28816f19d7c0f1816664980c0a9875d01b27cdf8eedddb639d9e108ad", size = 239266, upload-time = "2026-09-30T04:36:29.434Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/6d/9a08d7e0b29b7208e2c6c01dc56c8e0520e7c7beadbbfb024b58fd69c8a5/charset_normalizer-3.5.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7967d08cf06dee78443b874f98c98036f624f3a4e73e11f9f64f5be4d25393cf", size = 206783, upload-time = "2026-09-30T04:36:30.872Z" },
+ { url = "https://files.pythonhosted.org/packages/82/44/b0aa350280e6ff5a5492d17cf10460dd39d5ee848f872f7ba2df10607f60/charset_normalizer-3.5.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c2b5031f63e331e3839b40aed2dd6f191e9c07edbde303e7876846ea1946995", size = 210449, upload-time = "2026-09-30T04:36:32.625Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/8a/40db9aa9f5907bb0e6f8b6d64064bf8852fb33d4b813ff9414911df7647c/charset_normalizer-3.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fcff63213e8e6e47770541a4607175404f47cbb3ebea7b6058cc82d524a0e424", size = 367815, upload-time = "2026-09-30T04:36:34.197Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/72/9c5e7707b57c8ddfa9ddf7b0b1d009d7fbab9e9e887d5b721060f37e307d/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13", size = 247095, upload-time = "2026-09-30T04:36:35.803Z" },
+ { url = "https://files.pythonhosted.org/packages/83/09/71e453691e927de4ddf792770cfaab3f49d494e222f66ea5e404bbd5e39c/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a881931aa470808df94a8c380eed2bbbc76cd9dc622310f99665658c821eb6d", size = 233942, upload-time = "2026-09-30T04:36:37.407Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/86/85c84e4da8b27dd409577d9437926ff581c5f9d3c66038dc68c1a526de51/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8024d00c3faf3fc0c16e07a69f4405e8eac7cc0ab15f65fe6cf43827c4cf72b4", size = 273762, upload-time = "2026-09-30T04:36:38.904Z" },
+ { url = "https://files.pythonhosted.org/packages/92/08/564955a4b5f2ccb410ab480bbe8c6a18063ff27f2d35458731c4a5335df9/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4d48f2d08b9de5864e2c8744d4461b862fb149a18274abc8b698c45975573438", size = 269514, upload-time = "2026-09-30T04:36:40.469Z" },
+ { url = "https://files.pythonhosted.org/packages/18/24/bad3ac4271589df29cf5ce2f5ae490518a5739358052bd0d61209e6fea54/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34276fd796040bf0993ab33a369aa572e6979c7aab225a88893667ad8eac8f7a", size = 255056, upload-time = "2026-09-30T04:36:42.02Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/3e/350d89ad49916b86554d6f5f2d03ec1152148f87e5ff735106c6a03b1a36/charset_normalizer-3.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0521c5665880b33d603717defa76c094048900010897909952397feb3039da56", size = 251311, upload-time = "2026-09-30T04:36:43.577Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5b/4970a2d154df502e133402906dd04e3ae7cada7b3011283c88d0479a2585/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:eff0ac9dbe711a4aee69bf04a83896aa9b85f19641264053a9f6d48573abb7dd", size = 250190, upload-time = "2026-09-30T04:36:45.185Z" },
+ { url = "https://files.pythonhosted.org/packages/88/8c/f1a91bddc8fb47c2889e29ea7ea49a194eb0d9868675d786806519c00d76/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1503bccbeb36d5527790c3930327704c39af22de3112f1b1666a9f3ce15ee204", size = 237098, upload-time = "2026-09-30T04:36:46.689Z" },
+ { url = "https://files.pythonhosted.org/packages/24/0e/bb5dace3cc7e79068425386a6589c19b5a2ab5fefc2a46abea6919683332/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:52aa6992700996af31f375de0c6bacd402b0097fe40b53c426b9f51a90ebabc7", size = 274228, upload-time = "2026-09-30T04:36:48.31Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/79/b849ad523017ea9f5a45581bbebed91439e0cf42fd2860a6f64e358eb5a6/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e09a3942ecbdee5cce73ea9d42da82b81b72ac1bf031ce069b93b5adf4eac8cd", size = 252401, upload-time = "2026-09-30T04:36:50.091Z" },
+ { url = "https://files.pythonhosted.org/packages/89/8c/75469d690cf47200bce8f6cad7655724fc23148e147abfc5ce78b5f65863/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c7c9ab723cde841fefb34efbad91e87f00a674b1fe1cd0784fde742bf2c154dc", size = 271846, upload-time = "2026-09-30T04:36:51.719Z" },
+ { url = "https://files.pythonhosted.org/packages/26/cd/6d52d3c7437cdcf2e310ce9f28f282e733d4ef60ed19105d1819c356255f/charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ddc7dacc8ece3a182e7f15cb862d1fd616b46d076cb1ae9dd232b2c38b655874", size = 258151, upload-time = "2026-09-30T04:36:53.234Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/4c/070b38bdb5f49a70199fce923ec0726a49536a63ab262abbfcaaf351110b/charset_normalizer-3.5.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:ee43c17b173d46a3212baa6ead3ae258eeabdae48c263a01ccf0218c366dd655", size = 143570, upload-time = "2026-09-30T04:36:54.816Z" },
+ { url = "https://files.pythonhosted.org/packages/81/84/9ebfc8ed6c8c4fcd8e726ff6bf220cc8deb3966e31dce9be8dd8aa017e64/charset_normalizer-3.5.2-cp314-cp314-win32.whl", hash = "sha256:4f87960d57feabfb618e4e0af6e7371645fa26a277860739d6e5d6e0012c92f0", size = 186412, upload-time = "2026-09-30T04:36:56.643Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/78/5ed86f743d4bc350db307e7636419a0a5ee1d91806d30c7f667bd5c80dae/charset_normalizer-3.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4e81e09c1578b8df602e3db08b0b3ea0a6947ad612f52bf8dc5ea8d47691f0c", size = 210965, upload-time = "2026-09-30T04:36:58.205Z" },
+ { url = "https://files.pythonhosted.org/packages/53/94/a3a7698e9b1a395e1eb99ccd9a324be9347973bff4e72db2a06496d7cd27/charset_normalizer-3.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:80d02b6f04e92601a081dd97b23d3128033098bff5d35d392ddcc0476ea11253", size = 200406, upload-time = "2026-09-30T04:36:59.764Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/48/c5dd00d5ef7791f02666de250a5bb6071e29b7e133cf4b835800b6d3bc27/charset_normalizer-3.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:dca9ab98072a5a54ebacebdc45f53e645336b320c667410b061be1ca588ae709", size = 396871, upload-time = "2026-09-30T04:37:01.543Z" },
+ { url = "https://files.pythonhosted.org/packages/12/c8/8379554b42e8368161d898476686947a0fdbd3e8865170d7909dcabfdee8/charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0aa869112ef88429ae17820d99c3dd9504c9e9c671d3c246f3d7442cb051084", size = 249711, upload-time = "2026-09-30T04:37:03.111Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/eb/2ddb1035d17320caa9f41682935123a9a250277b261c3efc86b2d2a21343/charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c0afc6800ba57ccc350374c5bd6150419915d95ce93cdbab2d783d75eaf30ecb", size = 231146, upload-time = "2026-09-30T04:37:04.721Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/24/2ecb4bde104322cd7859d6594fcfa74649f8d90b3221c9feecbef149875b/charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dcd882da75ef9adf94903b1e3b9419e8aa8fb4c7396822b834b9ef7fb96954f", size = 271552, upload-time = "2026-09-30T04:37:06.295Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/98/9d5f6ebc3aee9fef5d30b4aff11fb2ab7a1222b4064f8ef2c7c87cde217a/charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2e06a3a98f916dd41d27f3105e02e7a40181c98c94b9158733d03a6f80506c09", size = 269810, upload-time = "2026-09-30T04:37:07.905Z" },
+ { url = "https://files.pythonhosted.org/packages/09/e1/a3b06a10461b1b7628853c934c644e03bc28e42767116afb52f19a56519b/charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bd128f206a7752ae1f2ab6c61bf8a24ba28913a10df8b14c2637b973ff97a80", size = 259845, upload-time = "2026-09-30T04:37:09.554Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/d3/6f561f74a296cf27d61775a1dc665ad13f3bff6a798810ca05907f37a7c4/charset_normalizer-3.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c8f3d67aeaf55f017982b73683f0e7342ba2f6635a78f69ce89ebb26aa411e5c", size = 252825, upload-time = "2026-09-30T04:37:11.274Z" },
+ { url = "https://files.pythonhosted.org/packages/26/9f/69e13ca3b18f43e0eafcd34c04a45b732ae22a43b54a5fc9e119103356eb/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fe9753dfee015c570d73df76f899f18444d41388bffcde097deba51c4fadbb9f", size = 252659, upload-time = "2026-09-30T04:37:12.941Z" },
+ { url = "https://files.pythonhosted.org/packages/73/a9/ace29806a0dae18939919c76ba526472d83214afa101105fabff2cf30625/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:92888bb3187c5ba50500b00b3b310c9f2c651709d28036077680cb5255450a03", size = 238819, upload-time = "2026-09-30T04:37:14.659Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/c1/6116d52a2e3311ec80f21f5fb5e17b27405f10b9608af8f6e69516841a1b/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d008d90a7f2471519aef0c90dfbe73b3e6e4d5e66ac48e19154c17e89e98b604", size = 272973, upload-time = "2026-09-30T04:37:16.346Z" },
+ { url = "https://files.pythonhosted.org/packages/19/aa/9955c7e93bba10a9c7e8f7a5031b7ced66f3a1883a55c00712b8d5850ff3/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31f3930700408d211f13378ccbe1c40845d8da54bd0681fac3a9b5aae81c7aa8", size = 253692, upload-time = "2026-09-30T04:37:18.212Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/33/2a6ae7fdc1b10cb581cef91addd8cdfc5f40d50abb5702309369d5834579/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:2a925889534b3748302dae5dead07cc13480de1dac3aea80a941b729b471ef93", size = 268757, upload-time = "2026-09-30T04:37:19.877Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/22/80992720a0282cd39bba1db35868e6b9c22f41281160143a836544bc1d8a/charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f5ec61164adcec446f8969a3358ec3f9b26bbda3b9213e5586d219afa8df2915", size = 262220, upload-time = "2026-09-30T04:37:21.583Z" },
+ { url = "https://files.pythonhosted.org/packages/92/9f/181fd07e1bffea1d95cd80c84ac537354f50699c22cfc4d3c02b6fc16208/charset_normalizer-3.5.2-cp314-cp314t-win32.whl", hash = "sha256:598a11a2c7ebaa5334bf698bf29568c9c390abac6a154d8170fedecd1cea38c5", size = 197797, upload-time = "2026-09-30T04:37:23.235Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1c/25d8415ec1c4f2f41f1680435e4c87cfb378ff2f677d950946f2a45d0632/charset_normalizer-3.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:7fdde2c9fd9e3eca40631e024664cf2584272cc8f96308cbe5fdfc930f51d8bc", size = 222765, upload-time = "2026-09-30T04:37:24.891Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/b4/46b48f013dadfc0d0d33b375438e31bdf5a989dc68389c6bf627054d4df9/charset_normalizer-3.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d1befeed746d247c81127bb14de9dc3d30edb6e5976d34f83f86ed262b1d9105", size = 210985, upload-time = "2026-09-30T04:37:26.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e9/34e597dee616d0b8ee4b34d29399e85c2204ade174157a48505d42baa4ff/charset_normalizer-3.5.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:87475fabc8d9996fd9c27debb395e642e8c838d78a00b6e932227a0e06b81e26", size = 367676, upload-time = "2026-09-30T04:37:28.329Z" },
+ { url = "https://files.pythonhosted.org/packages/60/9f/a5d1c91c0263745e2cd344c5a4415d787c575501ab1d449f1148ac6b495d/charset_normalizer-3.5.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9409a8bf35cf78353942504b24a57de3d75b708997a1e4bd8db71ac8633ce364", size = 248414, upload-time = "2026-09-30T04:37:30.167Z" },
+ { url = "https://files.pythonhosted.org/packages/26/79/e697f77464748a3ee3cf490c83d592459400d4898380d66c38366b03080c/charset_normalizer-3.5.2-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:498dc3188ca05a68231ac3fdbfc7f57eb67e1343c30e0fea17f8218c1599b253", size = 244775, upload-time = "2026-09-30T04:37:31.964Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/87/3d42a42e18ea066e2513936fd678a00696e77878b5ae04528976abdbcb83/charset_normalizer-3.5.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e242bb1c5e76e97dfa9e7f209a71e93a01d7f19ffdd5cfbb2e2d55b4f08f8ab0", size = 273877, upload-time = "2026-09-30T04:37:33.661Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/76/8a28136f3938ba9836f84280ce0c4d61ed1cf15a036b2034900c62634162/charset_normalizer-3.5.2-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def79fa35ef0cef8d2accec024f4fdc7ead3012ff02f5215c783f39f03ef8cfc", size = 269455, upload-time = "2026-09-30T04:37:35.573Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/a1/4fbf5d0f0f1b2a080474c1cf9a2f12c4c6531bb0e8ba591055e846d2b4e9/charset_normalizer-3.5.2-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df041de8887954562c9b261cba85ca0e9ded74048daf125f45edcfaa4832229", size = 257336, upload-time = "2026-09-30T04:37:37.397Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/a2/8b50aa320adb880ad579518e6f718f24944804b42a88b83d267d5d444125/charset_normalizer-3.5.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04851f73ae72b8413dddadb16a49dfee95263553741fd42d546f7d66907e6be5", size = 257584, upload-time = "2026-09-30T04:37:39.522Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/57/50e3fed84e175f40349bd0da7a4fce94c87f0378f52d74f511d89e0bdc20/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:183b88127acdb4fabe59d951ab424faf1af7b63cdbb5f776186c1ea2ffcaed98", size = 251854, upload-time = "2026-09-30T04:37:41.23Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/54/f7fbb3493c9f49091213b9c2d6dd65800696f1ce1a3f196a4205f50417b1/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:16fa0eccf81304b79c5cd87f9271c3b85dd9dd99245e4422ae9c0dd45e0f99d3", size = 248767, upload-time = "2026-09-30T04:37:42.883Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/37/b3a6385acc5a1e45b39ae9c90bfb9cf838a09b9dd37ef2740ab4c6b4a2eb/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:7441d755b7ab94f8d4eb3e43ec05482d760842fd263d003a99102d742cd835e2", size = 274479, upload-time = "2026-09-30T04:37:44.658Z" },
+ { url = "https://files.pythonhosted.org/packages/89/44/809913e2cfd279e635a9294fdbbfb1b1dc62a8189d473d561f649fce98d8/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:ca403d7e4798f525fdfc78e258820419cbbd0f0ecbab9de7840e3c017cf6b8cf", size = 258310, upload-time = "2026-09-30T04:37:46.529Z" },
+ { url = "https://files.pythonhosted.org/packages/af/a2/f28400ab13359d91bd39179df8e149376b9bf36588e739a3a4f9de2b84b2/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:df29a0a7107f7011e77f4eebdddec4c7331e24d787a0b21a46d63bdf7445da95", size = 271100, upload-time = "2026-09-30T04:37:48.399Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/89/9bab37955edf0adb3b66f8a3a6617d9f2f487e0d56f295a6a286cb640aa6/charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f3c96f633825733f735c5a9cf21d21a257d8e1edf0b1cee0a064b9c424ca0f7d", size = 259936, upload-time = "2026-09-30T04:37:50.023Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b5/4459e08d45a679f903d50fea08bc52cfa728cca4d7bd02c757b5e5abda2e/charset_normalizer-3.5.2-cp315-cp315-win32.whl", hash = "sha256:281cb91036248400f4cc957495cccd44c275c2e0c5854f7e45ac5cf7dc193847", size = 186301, upload-time = "2026-09-30T04:37:51.722Z" },
+ { url = "https://files.pythonhosted.org/packages/98/e8/55d5fd3935b4bce6da4fe0df61898e8c82653e317e677bd58aceb9c60f13/charset_normalizer-3.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:89b53f3cda69831909888e0494f4fa0bcd3537e3e138dabeb620bd6ad946bae8", size = 210858, upload-time = "2026-09-30T04:37:53.427Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/5b/974423c2fd8e524c7a7f64318c1e02240ef954912fa2b4d70344107b9c68/charset_normalizer-3.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:6be488a102b8cf28d0391d8c4ba7748938ae28b78ad901f8585520fca33ead1a", size = 200309, upload-time = "2026-09-30T04:37:55.015Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/f9/00ee0195db1013d8f7c416fd770fbeb560bb46eb2e36b054d05cb56f6cfa/charset_normalizer-3.5.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:915563965d418f986e7e145accc592eae9e1a1be3566ff98a05d7a9ec42a76e1", size = 395343, upload-time = "2026-09-30T04:37:56.743Z" },
+ { url = "https://files.pythonhosted.org/packages/04/3a/c00b50e94c964cf934c7899cd47c97952fc11dad71cc5884b3c61795b09b/charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65cd72beeeca9d3aaea1201e5923859f308f952f9c71de93f06063c79f0f7a3b", size = 250114, upload-time = "2026-09-30T04:37:58.607Z" },
+ { url = "https://files.pythonhosted.org/packages/50/27/d102dc880bbcffd0479ab64dfc1fb96777a854355a55e2bda72a71efadcb/charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b7fd005a73d9e657273b7a10dc71a9e03c8fb9ee6999798d6918ce095b81ac7f", size = 239997, upload-time = "2026-09-30T04:38:00.511Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/4a/bf7ef45794dd293fab5f98a9309817977fbb845b9998f171b8cc5d8437a3/charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e54da4baf05720032d527874d40b65fa4d7e5c6c6a43d0c3adbeffcaf275a2b3", size = 272313, upload-time = "2026-09-30T04:38:02.509Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/ee/008a2837737991474c5754bb3191010007663860979701990982a502cbaf/charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:124fbf1a8ff966d87ae05bb8bd45a71f966055ed8bba320d0c7cf450bc5f4d0e", size = 269986, upload-time = "2026-09-30T04:38:04.435Z" },
+ { url = "https://files.pythonhosted.org/packages/93/ad/bd74a283940dc910c5b14f8e4f80a248082bc9c0fcbe1f54530cb6d9cc5e/charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28b4f0d66fb834ff90f28209ac7bce77868c45d8c93e26f906709d9b7c2e1af9", size = 260202, upload-time = "2026-09-30T04:38:06.549Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/7b/ed341c66f69f688723501fac752be3d63c7159ca0d0d4174fc611e5710bb/charset_normalizer-3.5.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58ca3755ee7ff7f59b57789ec9833c9de9ea275405cdd240eda1f193112e398a", size = 259083, upload-time = "2026-09-30T04:38:08.311Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/9d/e41588b777965e5031a43128a1e96173ebb35ac75fc53ec3b517e7c21cd4/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:443eae2bf318abeaf6f15d785138f71fd6de770e99a92158b8b814265e079115", size = 253081, upload-time = "2026-09-30T04:38:10.402Z" },
+ { url = "https://files.pythonhosted.org/packages/81/35/b761eb6d8c1eb218b9b42b9b4d5ac902afdc399fb6dac6f9a9aac7bda589/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:58f361dcbab699cf8f42db3f47c8e7fd1036f138c23a5d08de9fde5f425a730c", size = 248712, upload-time = "2026-09-30T04:38:12.317Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/2c/147169a041b747759f37405c0a97157e8e92de967968373101ff14915cba/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1b4cbc7c3491ccb4aa17fcd8165649d01cf39f76de1696da8631b5f71b85401d", size = 274176, upload-time = "2026-09-30T04:38:14.138Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/2d/0ff8db0d373ba8538db686db11cd7e8912031490b9e4f383b41912e8d594/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:ba0b1d2620edf869789c3879223f52bf2afc5d31b3cb47cc57b3a12c05e2aa9d", size = 259413, upload-time = "2026-09-30T04:38:15.841Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/8e/b4a085fb47c9d3a7e43576a4784fdd8fe23f907514a972de8086edaf7a48/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:5e2b6b57e9733d39f0c9fd3185efa6b8e29652c4cd8fe94180272cf6ed9a78c4", size = 269434, upload-time = "2026-09-30T04:38:17.626Z" },
+ { url = "https://files.pythonhosted.org/packages/83/1c/d8d8d7322a7c3eecdf3237a4a419cf41d2eaad8e006ce7dfdd9d4c8fa2eb/charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:51cf45226a9b588d0d2b4880c62d686934b63ab0bd79ca23ab0e9762eb27441b", size = 262716, upload-time = "2026-09-30T04:38:19.214Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/16/0e4c6ba9b44e97a2da150e52d331e8f9c968b21b358fbffa6c856cebcd89/charset_normalizer-3.5.2-cp315-cp315t-win32.whl", hash = "sha256:5fb29fb8cd1a46c27a1bf9613ad5ec2599310d46b4025d9556404a6b6a292800", size = 197159, upload-time = "2026-09-30T04:38:21.037Z" },
+ { url = "https://files.pythonhosted.org/packages/be/33/e90bc2b1374f7f36ef106f56620de5a783907e19ca857efe2277e31cac3e/charset_normalizer-3.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:a192e2c40070d92c3ccf777e3a5c4ff515573cd2bb7ed0c537fdadbbec5bbf21", size = 221322, upload-time = "2026-09-30T04:38:22.886Z" },
+ { url = "https://files.pythonhosted.org/packages/66/89/dfa6dcb08c200b7830ab56439e8c1890f2971d51aafbb3937894a2e7fcfc/charset_normalizer-3.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:749e97e1b32313717a565abbe321bc2190bc8b35f1a67e4cdbc7c56c8d8ffe58", size = 209192, upload-time = "2026-09-30T04:38:24.648Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/ab/176fbfd5b64939c55d652366aa5b9ef1d767af207a3aa6ebeb0d226c484d/charset_normalizer-3.5.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd", size = 331815, upload-time = "2026-09-30T04:38:26.216Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/84/371eac6b30bdbcbf2d632a1a01809103459216fcaae61b8b8d922c1bfb8a/charset_normalizer-3.5.2-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7", size = 253276, upload-time = "2026-09-30T04:38:28.032Z" },
+ { url = "https://files.pythonhosted.org/packages/43/6f/c4fbae58febff71709c51bc7e18fdfa55341dc382704740f9f0cbf03817b/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d19fbd981a488e22cd04883659ca6b08f50b5974f9fd7c95655ef6a043e5893f", size = 241239, upload-time = "2026-09-30T04:38:29.732Z" },
+ { url = "https://files.pythonhosted.org/packages/61/71/458c3f42164a07d0c5210798e9e704b39e540a6793b05aba67f3a35243a9/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0fed1d06615f022ee3b13caf5e8b180cfea32bb2c5aded8a9d44277afc040f93", size = 231121, upload-time = "2026-09-30T04:38:31.462Z" },
+ { url = "https://files.pythonhosted.org/packages/09/54/ab9e89367076f6331bb6c65c4bf14a5361fa5191cb6561bf534f18504e1b/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:838dcc90063569a0448120554591a1d6c4a4ffe11babf048908793154ab86ade", size = 260350, upload-time = "2026-09-30T04:38:33.239Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/c1/061431ecc688d9d76602502cb57cc01e691e682c18f1beb45f9673b5bbd2/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ce45c6627b22c47e390bc91a41c3d13032192e699fa0bea96e9671b373d69b0", size = 255430, upload-time = "2026-09-30T04:38:34.865Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/1f/20c8949f0676f7ab811abdeb7f4d7f1cbc6e61ff20bef08b44edeb092bc8/charset_normalizer-3.5.2-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0774bf9bf620249fee3e0b8b9fd3065de213be30f3aa94ce2494b3b638949e26", size = 250612, upload-time = "2026-09-30T04:38:36.649Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9e/46f2fa4c431fc98c4ae76a8cb5bdca54e0341e3cfc3fcfd8e82740250818/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1db38f4c5496827c1a501846d64d14c3b80c7e6714e406cd7dc36a9899fa1011", size = 242083, upload-time = "2026-09-30T04:38:38.26Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/39/559be29a0c0f086e0bba6922babd38916cc5e0b58ced4de13ee01ea05508/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:304d8e4d493af723536393eee0c689eb7813f4a474c8b479dee63f1fdd98f621", size = 232738, upload-time = "2026-09-30T04:38:39.81Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/6c/387b0e4f756a282831c1d9fc6aeb6c51ca4507ca202767c8de15ce9b12e2/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:9b7f416ff0978e2f2249330527f0ad6fa02f4932e6199692d3b52da2048c19e4", size = 260703, upload-time = "2026-09-30T04:38:41.346Z" },
+ { url = "https://files.pythonhosted.org/packages/96/92/1fdf015f09ef449f50d3ac4b67c90887c9c318b727daa95cc4f866e6521d/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:01077390b03f7988f11d700a2194e69b119741a86b1a638b1db88891e3eced8e", size = 247622, upload-time = "2026-09-30T04:38:42.937Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/3c/8e7b8a5671ad5d433669fb2a76f1a0164df2d9b1718b0206bc2a16d840cc/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:7e841fb9010836c992c9f12fcbd43a831de93a5f726fc1ccd8ca1d0268c5014c", size = 257500, upload-time = "2026-09-30T04:38:44.604Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/f0/45b579df5cabc1d5d53ea1cc35e8437d3ca768c0acccc7041517cb6fbb32/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9cae88599c7219005d879f98e5ed53341e9a122af585e1091200358a3003d2a0", size = 255100, upload-time = "2026-09-30T04:38:46.289Z" },
+ { url = "https://files.pythonhosted.org/packages/31/68/fdec18a343f5fb3f310588dd478b09ac4799e0b187dbade3a8cd776f03ef/charset_normalizer-3.5.2-cp37-abi3-win32.whl", hash = "sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf", size = 174499, upload-time = "2026-09-30T04:38:47.999Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8a/b618149cc5207943a0242068d7a27897f56a62947b5a039085f2a22029f8/charset_normalizer-3.5.2-cp37-abi3-win_amd64.whl", hash = "sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036", size = 200092, upload-time = "2026-09-30T04:38:49.707Z" },
+ { url = "https://files.pythonhosted.org/packages/03/cf/4c66866fa9e2b1c78e3c911516d1de497a677b7ac60f1eceda74ce777ca3/charset_normalizer-3.5.2-cp37-abi3-win_arm64.whl", hash = "sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e", size = 294363, upload-time = "2026-09-30T04:38:51.312Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685", size = 68872, upload-time = "2026-09-30T04:39:21.828Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
-source = { registry = "http://pypi.i.h.pjlab.org.cn/brain/dev/+simple" }
+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 = "cryptography"
+version = "50.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
+ { url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" },
+ { url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" },
+ { url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" },
+ { url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" },
+ { url = "https://files.pythonhosted.org/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" },
+ { url = "https://files.pythonhosted.org/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" },
+ { url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" },
+ { url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" },
+ { url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" },
+ { url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" },
+ { url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" },
+ { url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/7a/f08d34ce09d60f89ebd391e2ebc6ba2b995e6dd7552f41820f8085f94e53/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67", size = 4716035, upload-time = "2026-09-30T15:29:48.681Z" },
+ { url = "https://files.pythonhosted.org/packages/45/67/e18fb65592451a2acb76e9f2fbe14e0f47a8318b4c5430f1633851d03daa/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a", size = 4726917, upload-time = "2026-09-30T15:29:50.608Z" },
+ { url = "https://files.pythonhosted.org/packages/83/28/38fdce17e60f6b825e69fc3b7f75e70a6612759980704697e1de4cbfaf6e/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48", size = 4715341, upload-time = "2026-09-30T15:29:52.522Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b1/d9121a717e0f893c64bd6ca7702614778d7df2a5c309128a002421788516/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42", size = 4726322, upload-time = "2026-09-30T15:29:54.263Z" },
+ { url = "https://files.pythonhosted.org/packages/36/8b/e6d153808bf353e152abd2fd4d8f09670d956ac78379ac46e60d7efbf04c/cryptography-50.0.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81", size = 3873097, upload-time = "2026-09-30T15:29:56.097Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/1d/1271f287ff7170ddafc2aad36260c4eec20ccd2fea70f38455e9d56d427b/cryptography-50.0.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452", size = 3805376, upload-time = "2026-09-30T15:29:58.729Z" },
+]
+
+[[package]]
+name = "deprecation"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" }
wheels = [
- { url = "http://pypi.i.h.pjlab.org.cn/brain/dev/+f/4f1/d9991f5acc0ca/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" },
+ { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" },
+]
+
+[[package]]
+name = "dirhash"
+version = "0.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "scantree" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1d/70/49f93897f3a4f7ab5f20a854ebc91aad47854e9fb2cd169e3a4452fa3f5e/dirhash-0.5.0.tar.gz", hash = "sha256:e60760f0ab2e935d8cb088923ea2c6492398dca42cec785df778985fd4cd5386", size = 21377, upload-time = "2024-08-03T22:14:13.322Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5e/1f/c8bf92552b7f0a13b9f12b85e3de8df6d9814240e0f8ce8f37433df028b3/dirhash-0.5.0-py3-none-any.whl", hash = "sha256:523dfd6b058c64f45b31604376926c6e2bd2ea301d0df23095d4055674e38b09", size = 13119, upload-time = "2024-08-03T22:14:11.688Z" },
+]
+
+[[package]]
+name = "distro"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
+source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { 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 = "fastapi"
+version = "0.157.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "opentelemetry-api" },
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d3/7b/f9540549feb36e84c3420aea470148d0f0bcd1e9d1aec5b36216f3f31738/fastapi-0.157.0.tar.gz", hash = "sha256:460d48b9fe6fabbb785630fc5b256710bc82723b597b4bfbd6e4470ae5bb20f4", size = 504894, upload-time = "2026-10-21T13:56:33.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/50/c3/759716158338892ed8e483077241cdf80e1fd727f56baf750c75bfea58de/fastapi-0.157.0-py3-none-any.whl", hash = "sha256:45b5cbf098ce3f47e008ca67931ce333d028129c823e3b6766a3bd0d70b81f7f", size = 147897, upload-time = "2026-10-21T13:56:31.895Z" },
+]
+
+[[package]]
+name = "fastuuid"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" },
+ { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" },
+ { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" },
+ { url = "https://files.pythonhosted.org/packages/98/f3/12481bda4e5b6d3e698fbf525df4443cc7dce746f246b86b6fcb2fba1844/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34", size = 516386, upload-time = "2025-10-19T22:42:40.176Z" },
+ { url = "https://files.pythonhosted.org/packages/59/19/2fc58a1446e4d72b655648eb0879b04e88ed6fa70d474efcf550f640f6ec/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7", size = 264569, upload-time = "2025-10-19T22:25:50.977Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/3c74756e5b02c40cfcc8b1d8b5bac4edbd532b55917a6bcc9113550e99d1/fastuuid-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1", size = 254366, upload-time = "2025-10-19T22:29:49.166Z" },
+ { url = "https://files.pythonhosted.org/packages/52/96/d761da3fccfa84f0f353ce6e3eb8b7f76b3aa21fd25e1b00a19f9c80a063/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc", size = 278978, upload-time = "2025-10-19T22:35:41.306Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/c2/f84c90167cc7765cb82b3ff7808057608b21c14a38531845d933a4637307/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8", size = 279692, upload-time = "2025-10-19T22:25:36.997Z" },
+ { url = "https://files.pythonhosted.org/packages/af/7b/4bacd03897b88c12348e7bd77943bac32ccf80ff98100598fcff74f75f2e/fastuuid-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7", size = 303384, upload-time = "2025-10-19T22:29:46.578Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/a2/584f2c29641df8bd810d00c1f21d408c12e9ad0c0dafdb8b7b29e5ddf787/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73", size = 460921, upload-time = "2025-10-19T22:36:42.006Z" },
+ { url = "https://files.pythonhosted.org/packages/24/68/c6b77443bb7764c760e211002c8638c0c7cce11cb584927e723215ba1398/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36", size = 480575, upload-time = "2025-10-19T22:28:18.975Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/87/93f553111b33f9bb83145be12868c3c475bf8ea87c107063d01377cc0e8e/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94", size = 452317, upload-time = "2025-10-19T22:25:32.75Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8c/a04d486ca55b5abb7eaa65b39df8d891b7b1635b22db2163734dc273579a/fastuuid-0.14.0-cp311-cp311-win32.whl", hash = "sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24", size = 154804, upload-time = "2025-10-19T22:24:15.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/b2/2d40bf00820de94b9280366a122cbaa60090c8cf59e89ac3938cf5d75895/fastuuid-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa", size = 156099, upload-time = "2025-10-19T22:24:31.646Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" },
+ { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" },
+ { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" },
+ { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" },
+ { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" },
+ { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" },
+ { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" },
+ { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" },
+ { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" },
+ { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" },
+ { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" },
+ { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" },
+ { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.32.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/59/e19834834cb01a32febfbb0f8a23a9088088f5d45991824ff2bc3b5e8acb/filelock-3.32.7.tar.gz", hash = "sha256:37b8a3d9811b0f9aef7e5ec5c71bb320de52df51e6ca9bcd6f5ad81187660da7", size = 225154, upload-time = "2026-09-16T00:24:20.907Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/df/31098c5aeb4d966b553641472bd55fcf5fdfac953549894b8a765ba44e91/filelock-3.32.7-py3-none-any.whl", hash = "sha256:65ff0d0190ea42038b32bda4b77834fb05be2cad4c5b9b01aa4dfb3614536e52", size = 100157, upload-time = "2026-09-16T00:24:19.543Z" },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" },
+ { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" },
+ { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" },
+ { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" },
+ { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" },
+ { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" },
+ { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" },
+ { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" },
+ { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" },
+ { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" },
+ { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" },
+ { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" },
+ { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" },
+ { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" },
+ { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" },
+ { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" },
+ { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" },
+ { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" },
+ { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" },
+ { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" },
+ { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" },
+ { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" },
+ { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
+]
+
+[[package]]
+name = "fsspec"
+version = "2026.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/77/cd/9be253869fc42e764de7f3dedd6969af7d44ff9c3375214a3442a6f3fc08/fsspec-2026.9.0.tar.gz", hash = "sha256:0f08147951c8cb31d844c3547d631053b127863b60be04cf06e121333ee0e2fe", size = 333545, upload-time = "2026-09-18T17:50:42.825Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl", hash = "sha256:8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f", size = 221738, upload-time = "2026-09-18T17:50:41.341Z" },
+]
+
+[[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 = "h2"
+version = "4.4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hpack" },
+ { name = "hyperframe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/85/7c366e69d84c17bb778fe41419e1fbcce3033d5b7ce29bbffff0a98b859f/h2-4.4.1.tar.gz", hash = "sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516", size = 2157281, upload-time = "2026-08-03T11:45:09.509Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/22/e85faf23bd72a92d1921e37d674ca56eb298a3c8be31fdecef0ff2b3aaac/h2-4.4.1-py3-none-any.whl", hash = "sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6", size = 62636, upload-time = "2026-08-03T11:44:59.164Z" },
+]
+
+[[package]]
+name = "harbor"
+version = "0.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dirhash" },
+ { name = "fastapi" },
+ { name = "filelock" },
+ { name = "httpx" },
+ { name = "jinja2" },
+ { name = "jsonschema" },
+ { name = "litellm" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+ { name = "pydantic" },
+ { name = "pyjwt" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "shortuuid" },
+ { name = "supabase" },
+ { name = "tenacity" },
+ { name = "toml" },
+ { name = "typer" },
+ { name = "uvicorn" },
]
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/exceptiongroup/1.3.1/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219" }
+sdist = { url = "https://files.pythonhosted.org/packages/2a/3a/b05d0e8752d5d531e56fbb2955f0e656c0a9b5fbcd202dbad7313b29d4df/harbor-0.23.0.tar.gz", hash = "sha256:a8b87e98db59877228bb36d2c24757cc422a3144ffaf31a1f88955c65c911e4d", size = 1907464, upload-time = "2026-09-12T04:55:11.919Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/exceptiongroup/1.3.1/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598" },
+ { url = "https://files.pythonhosted.org/packages/19/c7/607ff037dff1f40d1f941b9854742d66fd43630274fd4e7b8de8480dad34/harbor-0.23.0-py3-none-any.whl", hash = "sha256:8747400dbb2a5e2298e1338e17e88eba38433c0433fd700f34d1a9021bba5c37", size = 2142100, upload-time = "2026-09-12T04:55:10.014Z" },
]
[[package]]
@@ -42,61 +984,1163 @@ dependencies = [
[package.optional-dependencies]
dev = [
+ { name = "httpx" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "ruff" },
]
+sandbox = [
+ { name = "harbor", marker = "python_full_version >= '3.12'" },
+ { name = "httpx" },
+ { name = "opensandbox", marker = "python_full_version >= '3.12'" },
+]
[package.metadata]
requires-dist = [
+ { name = "harbor", marker = "python_full_version >= '3.12' and extra == 'sandbox'", specifier = "==0.23.0" },
+ { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.27,<1" },
+ { name = "httpx", marker = "extra == 'sandbox'", specifier = ">=0.27,<1" },
+ { name = "opensandbox", marker = "python_full_version >= '3.12' and extra == 'sandbox'", specifier = "==0.1.9" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
{ name = "pyyaml", specifier = ">=6.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8" },
{ name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" },
]
-provides-extras = ["dev"]
+provides-extras = ["sandbox", "dev"]
+
+[[package]]
+name = "hf-xet"
+version = "1.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/27/06d899ea7bd721d272f84aac98bdb238de98af4cc767a69056d967d68c71/hf_xet-1.7.0.tar.gz", hash = "sha256:d406ec79053c0871817f700c2ac8c36ba0d87f9c34b7458b0f0063bb218b0466", size = 985689, upload-time = "2026-10-06T20:18:43.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/7c/3e45174942e6793adde6cba4daa7fb037275cf02a944d9eadfcf9ff33b86/hf_xet-1.7.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:fa029678be1ba7f953c409b0b27bf15cc69cd1c9b3a674fbd78856ebefca1052", size = 3803919, upload-time = "2026-10-06T20:18:09.844Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/3a/5e8b363391adcbb002e191dbf924dab31464ea9c45adfeb73502afc36d35/hf_xet-1.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57bc157b8b7fe3bee9dcb9af7f3da8de41801c3b31a9ef68a77a33c6a6be382f", size = 3553588, upload-time = "2026-10-06T20:18:13.376Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c2/0d1eaa5da13bbf9c896badc7f380601c7d973a87a6ffb4d100267c4536c1/hf_xet-1.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:87dab080f8f7d32781c2586904e3603f4e60d09bfc727706c3ae419e0829beeb", size = 4201962, upload-time = "2026-10-06T20:18:16.11Z" },
+ { url = "https://files.pythonhosted.org/packages/23/2d/225d5b11a9ca7d31b9470a57f2b2be1a5cef8b84325a2146aeb4589e226c/hf_xet-1.7.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b01fe18dbbd151a2403d2c64ed30dc6547b00d6babab9a617d77c7acdb81ee66", size = 3982978, upload-time = "2026-10-06T20:18:18.092Z" },
+ { url = "https://files.pythonhosted.org/packages/93/34/9d681f0e3dac0b5dae0d7dea748429266f24e52415446523f464fbaa828e/hf_xet-1.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4ee5e05a627f5ab5bad7a86582277d645556ea1e199903aae19e033a392aa13a", size = 4181558, upload-time = "2026-10-06T20:18:20.082Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f0/277f039b7d72027bc2ed277f1b62a2f70f740a5aac2a3e7243e5b6854c5d/hf_xet-1.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:19c0e64f14175ccb6a1aff69e0d2ab9ec5269a560e6687abaf2b3fa4f73de7cd", size = 4411546, upload-time = "2026-10-06T20:18:21.999Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/7f/832d3ddb49326114175b7bcc50daea8565c09fd21ac03a02b211c09fefb7/hf_xet-1.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:757168feb5679647c0bb13ee5d0faebe799c4dff9051419885a566ebd79f949d", size = 3812809, upload-time = "2026-10-06T20:18:24.288Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/c4/310c3c29e5beae7c049e63947bd1923d597883b41c9ec4718589920812c4/hf_xet-1.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b91569d5f1b61c34b043687da02c05dd3604f3d329e7868510bf3f7971599006", size = 3646174, upload-time = "2026-10-06T20:18:26.279Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/0b/b03be21ffaada749ba0d3197d8aefbf1aa698bac149580421c15239b299e/hf_xet-1.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e3e88a7a75d7d95cbee1f37dc31341d6201124cf21c6c4b1dfab8ccba9b09e0f", size = 3796096, upload-time = "2026-10-06T20:18:28.43Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/47/a26ebdce7056a61e931f228439bc0ab08cbec239d1690f965e5e637cba79/hf_xet-1.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:59fba37039233c7fcbe196817d6cdcf1b40dfb17b410f229d85b0cf0a1848da4", size = 3560352, upload-time = "2026-10-06T20:18:30.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/4c/2bf3b66c215d409655f28de1622393dde04c9461280d48c7924bb3b2decd/hf_xet-1.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2814a6e999d13464c4d679b788cc5d784eb5a4edfc638a31f10e9a11ab531ef8", size = 4212180, upload-time = "2026-10-06T20:18:32.292Z" },
+ { url = "https://files.pythonhosted.org/packages/49/0c/a2f703a5a78267556e89e03316fa0805c86b72b50829bc67665746e8ebf0/hf_xet-1.7.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fcfd6c22418e57dd5b3aea649e813b2e2cfb2aebf317b210d90f1fe4b3018b52", size = 3990011, upload-time = "2026-10-06T20:18:34.21Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/77/e52e4201b1cbf571530a61cc57f70182045a39a230089ee5f1df182a4de2/hf_xet-1.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80f79dae613ce9e0ea1fd1ae15616ca9ac74aed4c770aabc199c4f03ebecc863", size = 4190628, upload-time = "2026-10-06T20:18:36.062Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/dc/03a21b89f118664a0926ff25b0f8e44a519bf22724a6a8fc7a9abbc188b6/hf_xet-1.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a9e802f33bf50c851abe45fc5380e61f959e2d369647d6742b79ad9d6c27cab", size = 4418814, upload-time = "2026-10-06T20:18:37.888Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/59/b35106dfa71b6eef605dc88bd038fe99c7f86fb132a15b60d0bf2f235b2c/hf_xet-1.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:2b7bb5727889b0f2436dbaaad8fc4c3e66b8240d992716989e0c086b4278b1bc", size = 3822644, upload-time = "2026-10-06T20:18:40.052Z" },
+ { url = "https://files.pythonhosted.org/packages/48/cd/072313585f74fe9d441e2eb5e0a4703c30586cd709810ea369675f61b74e/hf_xet-1.7.0-cp38-abi3-win_arm64.whl", hash = "sha256:acc3851cf2576a8fb2ae926da863f4efabe21303cf292e9a44332802ab0dcc6a", size = 3662436, upload-time = "2026-10-06T20:18:42.205Z" },
+]
+
+[[package]]
+name = "hpack"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" },
+]
+
+[[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.optional-dependencies]
+http2 = [
+ { name = "h2" },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "1.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
+ { name = "httpx" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/2a/484d112c0d8fc5f665d7b65137ac9cdb2953c982391598c3597968a12ee7/huggingface_hub-1.33.0.tar.gz", hash = "sha256:367be21a201db9523eddf8aeac7048f2602c1b308691c97640d5e72ed188007e", size = 1043548, upload-time = "2026-09-24T09:49:29.971Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/16/963096d224b80909432dc16561a615fd33d2d13beef3ce4c63fa25e40867/huggingface_hub-1.33.0-py3-none-any.whl", hash = "sha256:04e434b06e100eddbce9a6e817d72693a7884b10a79bd67ab48080d5c07eb899", size = 846435, upload-time = "2026-09-24T09:49:28.059Z" },
+]
+
+[[package]]
+name = "hyperframe"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" },
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "8.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "zipp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/72/c600ae4f68c28fc19f9c31b9403053e5dbb8cace2e6842c7b7c3e4d42fe9/importlib_metadata-8.9.0.tar.gz", hash = "sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee", size = 56140, upload-time = "2026-03-20T16:56:26.362Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7d/f9/97f2ca8bb3ec6e4b1d64f983ebe98b9a192faddff67fac3d6303a537e670/importlib_metadata-8.9.0-py3-none-any.whl", hash = "sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f", size = 27220, upload-time = "2026-03-20T16:56:25.07Z" },
+]
[[package]]
name = "iniconfig"
version = "2.3.0"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/iniconfig/2.3.0/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730" }
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "jiter"
+version = "0.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9c/1f/8176d92e001f86505424b41664032ae26a882bc9ca41a32c803f373f9195/jiter-0.17.0.tar.gz", hash = "sha256:03e432f226a453851079fb84cd17c6da9991eab723e28d716f14ae3d906e0c12", size = 229037, upload-time = "2026-09-12T15:14:14.253Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/fb/0b68a8666a203ab349334e502285153ac97e84a1a3e2f80de8385ed0899c/jiter-0.17.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ed1a24005daac667d577402d75a2922f9775a165b146b883ff1ad3602d8be689", size = 290968, upload-time = "2026-09-12T15:11:10.44Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e9/5993079500530586655c6f2823e2926b3f5ceddd49918c5a86757eadba3a/jiter-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b847b18d066c46b3b7ae49d6c94a7634c5e4a8983146ee25562a092000f5e3ad", size = 324072, upload-time = "2026-09-12T15:11:12.03Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/56/6f5dbffaa5b0e647386e3c3376aa150987538f1b13f6da1a9582787957af/jiter-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b68d3495d95da120651a5628c7ebadee84ed001a1b76e6afc325c42482f15b5", size = 344891, upload-time = "2026-09-12T15:11:13.387Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9d/8dd5719add6c77be95b23c129682a1484c5339ffd66736d66bfa6831a2f3/jiter-0.17.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c1a5336c04a41b1f1cf9572e294aec27cc569767ff73de7bf87a91f0bea7cb9", size = 328213, upload-time = "2026-09-12T15:11:14.655Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/e5/82f885863afaa79efac066efdb7b70bd242ef83d3e548453d61b3737e54a/jiter-0.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b75f85660108965a94be77911a25a253429307294d9415b3c597118977a614de", size = 343033, upload-time = "2026-09-12T15:11:16.046Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/41/ee3cd7db1704ec9c274ae549803ef1369c801490f7f6d48d0aa3b9ac1ae0/jiter-0.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32aaaa764604496610a3ad2d98503ae88ccb2fbe769e892ff4533e778e85f708", size = 351701, upload-time = "2026-09-12T15:11:19.188Z" },
+ { url = "https://files.pythonhosted.org/packages/93/2c/af9ca503b38683a82b6732352a861515f28e2d46fe445aea57e07fc86e06/jiter-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:826871c42cebaae22f0a2b5673a4a1a75c851bb2d13b3c17764a630a6b298984", size = 348754, upload-time = "2026-09-12T15:11:20.486Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/ba/692b071b194270b019b7d1790adb8658193379b951029e5ad15a14a2809d/jiter-0.17.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:00b5a98df3e3a3e8cf7b619f4ac2f8bf975bbf3d95d02c5d17b8dbfe5c8b8245", size = 329263, upload-time = "2026-09-12T15:11:21.701Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8f/35a22beb6b9eb025b7e2f88a38ca3b9c87017aac237feec2583eabe243ad/jiter-0.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6af5b74073bd25bae695e6d00919f6a9be7ed5a9f8836d981eb1ffe84139e6fb", size = 337572, upload-time = "2026-09-12T15:11:23.145Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/0f/4341ceb3ce08199b4e8a798acc0bb0c79b3e861b6cecdb9d3b5138965c5d/jiter-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:16dd0c1baf098ae70b8f3616574eb3fedf34e26670b89e16a7e67561f737ed2d", size = 485174, upload-time = "2026-09-12T15:11:24.394Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/6d/92fa138ac82db9001b68563df204c32f2203bee84301df083260039ca23b/jiter-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:545c36a0f3b2238c242cc9785439d3242a871b7bc39fe3f441bcaa07bf3aa83e", size = 522115, upload-time = "2026-09-12T15:11:25.645Z" },
+ { url = "https://files.pythonhosted.org/packages/52/92/9b11557ddf898e84c253d1c42cc1bddd8ad1a754b46b01ae35a6a253526f/jiter-0.17.0-cp310-cp310-win32.whl", hash = "sha256:155be7355bdb7ca76ab0961be8982c225f964a5c073a83984183f22391cc29fc", size = 188163, upload-time = "2026-09-12T15:11:26.793Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/f3/b4381cc962fed7d505913d5920e9f51f1fd5cf340a563134e8cadf3604d2/jiter-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:37150a9e02e869475854fa20b7d0d5e26d18d0f8bc17293999973ff27e99ae7a", size = 231679, upload-time = "2026-09-12T15:11:28.911Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/50/17afdaffcc8af4bf4fddf2b6c26d066553aa2221983f2affcde435fc2532/jiter-0.17.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cfafd7be8b16ceadd298db542cead37cddc211c4c49e04ad2596924df18625b1", size = 290699, upload-time = "2026-09-12T15:11:30.085Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e4/c185d32d5b3657ad84da26c84a9eb15f00aa1b39d6882fcc0052dba2d7c2/jiter-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8adca2e793288e5f1bb29279bb439d0d3cfbb50eddca7e7e6ffd42ff4f482406", size = 324246, upload-time = "2026-09-12T15:11:31.3Z" },
+ { url = "https://files.pythonhosted.org/packages/24/7a/8b8903bfe91a90a8fa1ec9b45d9fda5b6287a386693b69d720a882d73f3c/jiter-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c692d567ba206c7cca38c9d1d0ccc70c9786290173c184d871ca12e9981ed7", size = 343275, upload-time = "2026-09-12T15:11:32.758Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/5d/6821fae2abc71a3c3a84bef8598d31fc4f27d9edfb55bd8f6c08afb8ef93/jiter-0.17.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81c83c0abe614446a283d994d2c07c4f58632dea2cdf66ba9e2921bb8ccd593e", size = 328034, upload-time = "2026-09-12T15:11:33.9Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/51/8e7a963b1c2dfdc01d6228b004f50a2a3d7c46f0549d7b096a5d15ef81d5/jiter-0.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:073dc68c1a700c8fc480e877864a6b6ffc887533e261f4380c08c16bf09d057a", size = 342457, upload-time = "2026-09-12T15:11:36.414Z" },
+ { url = "https://files.pythonhosted.org/packages/73/27/8b2a267e3bda45d9298331cacfe3521e761f0a2b05ad10a23c6548d08358/jiter-0.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:492f37230bbf9581ab2c17bcda862c249afb9ae2e3ab2dd6db59943bc4cc3153", size = 351271, upload-time = "2026-09-12T15:11:37.692Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/8f/5c74e5e142a6736833d7a991ab04d5c0738038dc44db05af0bb3cd2559e8/jiter-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5888fe5abc1ca2fa834a3e1b4c7ef0dcece286a7d7e95a609ef0934b777b9fc9", size = 346052, upload-time = "2026-09-12T15:11:39.722Z" },
+ { url = "https://files.pythonhosted.org/packages/98/9c/f54920f06d1696e80b1be841d56412871c6856b1b1e3b541b1ed35346554/jiter-0.17.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:84ac78df457e1ee3f7e733bd114823302ae8c5ad5542d7e6647d92ffaa090a04", size = 328915, upload-time = "2026-09-12T15:11:41.065Z" },
+ { url = "https://files.pythonhosted.org/packages/21/53/080f126863bceb055db9f1fd5431485eb493b35545a3c35e961ac18cd924/jiter-0.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7573e80232c5bcf80c24c038cf7e53a463f5c3b1dd1dd4109d66304f4dccc233", size = 337240, upload-time = "2026-09-12T15:11:42.356Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/76/3ab742823a0e0e70e143c6c90a482d9d90396ac2445e7b9483eb7245d3b5/jiter-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:11902505d401691720f5785c15b02204248526edee11b635cd6c40cd52b81599", size = 484917, upload-time = "2026-09-12T15:11:43.549Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e0/8ca71bc8b9cc9ed96c9da565863e00f3bb875a8fb82abcf03e975e067902/jiter-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:64846211a2debe7c071d2146d2283d2b0c1c93dc8fd5fb7794faac2ca6061b5c", size = 521868, upload-time = "2026-09-12T15:11:44.704Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/02/81f8719dcedb75713082a2048405376c81f6546b75af8167f3bba01a1ed0/jiter-0.17.0-cp311-cp311-win32.whl", hash = "sha256:c19b9357309b8cc6de8a48fca8e44a8c9c2feaaa2f5896d037fa505d48fcab80", size = 187844, upload-time = "2026-09-12T15:11:45.874Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/8c/59693f348488f01ed12d862e99ab8da14961152d3e9c39b9b1ef363f3572/jiter-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:e654b6b04e39c9cb19cb8b04c6ddf1f2db07751fa14156413969fd78bad0e5cb", size = 231402, upload-time = "2026-09-12T15:11:47.083Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/89/fb35e286463cb9f01edc2c4e47df6e5477ee36bca5096414e9ea87985588/jiter-0.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:3ad556afc289f15d2b181b941982d01f06190863c07440185b9f354e1bd2def3", size = 187006, upload-time = "2026-09-12T15:11:48.245Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/f8/07bd8c3a23f7a8a6875e6a820bbffe1483a18f18f9398a91b5495123176e/jiter-0.17.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ebf918dfd6a74adc1b9ad71f63c4ab00902fcd3b7fd39f2e24d871db8d713b91", size = 291633, upload-time = "2026-09-12T15:11:49.431Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/5e/0de4c6f84ffefa6809ffc2d550b9a314365acf7e7ec9b6c7375d49047900/jiter-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61aed66ee042b3b49ef85fdf75714234d055d89d8496ac1c6e47f89e7a30d5e4", size = 321695, upload-time = "2026-09-12T15:11:52.727Z" },
+ { url = "https://files.pythonhosted.org/packages/20/ac/befe2e82065bee37a0252081666ed2f48c1ac5f5c6c318c2de8168ba393d/jiter-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76eb4a5c20e86f9f848286f167024890f2862258a965d254774deb7fc1545ca1", size = 341967, upload-time = "2026-09-12T15:11:54.231Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/cd/9797c1e529746750ae589da7c1a8c24373f00d88e11a989f9e5eb1959079/jiter-0.17.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bcc064f99183a9cbe7f26ed648c352031a74145cd61ed75d34632c73eb46a5a8", size = 326546, upload-time = "2026-09-12T15:11:55.41Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/fd/e6914c38d6347bab4ebff2b1f0c0f191db276e7a1d5c376176757da42fe3/jiter-0.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73b64e69c4150748e020356d958af94bec33c70a0a93d665cfa8f6d580fe1a63", size = 340995, upload-time = "2026-09-12T15:11:58.211Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/7d/611b3abf6f88945b5474da5cdc6d1a185e805ac9bf446bb7766dcda6ea87/jiter-0.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0bc7f684b65bcda9c20434267577db71bf9905ceddd32b60d1d93278d8c8d3a", size = 352188, upload-time = "2026-09-12T15:11:59.414Z" },
+ { url = "https://files.pythonhosted.org/packages/52/f8/b6e513ecbdf3b3cebe587c2279281ecf775b729a58cf4cc7bdf898ded029/jiter-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c21265b251d99bbb40080d178a8953e35601d3a1564e05c4de4c0d2ca616797", size = 345025, upload-time = "2026-09-12T15:12:00.697Z" },
+ { url = "https://files.pythonhosted.org/packages/28/a8/fe26d06c5a6c5a4cfe703c5154c8a140da1305671eb3681aba9422d4f393/jiter-0.17.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:f3d7f7b34114f7ddc6d72a8e882d49de636b35d9fd12b4d420d3c5729f6c9812", size = 329180, upload-time = "2026-09-12T15:12:01.831Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/58/e6d66a26af40a20e62486feb7e222fd50f6e7aaa4f107abd89675dcc835b/jiter-0.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5078ab00664307fab2019b522a93aeb191122789f085daf5fd9e362154021d4a", size = 335805, upload-time = "2026-09-12T15:12:03.056Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/3e/96520aa2fef5ef831d95483a902140bfab83dcac9eaa74f7df61b5e50a1b/jiter-0.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:470e1b1e4c42f1ead2189166a299691871a2df5056c976e7fb96feafaf5f9d44", size = 484121, upload-time = "2026-09-12T15:12:04.414Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/8f/5d9d92fe538bf36ff481a2278c48147e59c1cf8eb2f7be665260665febe5/jiter-0.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6eb6aedeb7352b8f3b6af9cbd67983840165c00428e63f1b420a85885128ea31", size = 521310, upload-time = "2026-09-12T15:12:05.612Z" },
+ { url = "https://files.pythonhosted.org/packages/50/06/a09f979b22e652afbc3de66c709b2ba92edcef555f7535ab937c86b4f21a/jiter-0.17.0-cp312-cp312-win32.whl", hash = "sha256:362bb47423886d45a9f705d2d9d4008c6eedd4e41eb1bab4e96fb6daa06b33fd", size = 185029, upload-time = "2026-09-12T15:12:06.994Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/d9/98265a005b2473ec2be5a84e2b64c2f65382c673879f1574845cd4bcd77c/jiter-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:9bd3caac219df476dd0cc3fe01d2f1581ed588906feac767abd9614c1c12f8b3", size = 227381, upload-time = "2026-09-12T15:12:08.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/11/2e05bf5a56e57a543ebb8f585074adf09383e99d7b062dac92eab1f4d57f/jiter-0.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:36ee6e69027396664e59995b9a635a947a5304ee9837279584a0bb8145c8f6b8", size = 183610, upload-time = "2026-09-12T15:12:10.374Z" },
+ { url = "https://files.pythonhosted.org/packages/40/eb/2c4a8075ed5ea02b56911e9375d4c8d7784572ff4af32e5a99ae0d071044/jiter-0.17.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1b18434638228c0c184281609bf3d9459026a0f1ea48fb76c205e3ef72069caa", size = 290991, upload-time = "2026-09-12T15:12:11.641Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/b1/34bfa29599d420423baac6ff7cada6674fe63d5a7a2ccb3900b904678783/jiter-0.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ec89771f4272b989487a6364e519db6bbaba323e8bbf949ac89a45ea9c18b7a3", size = 321425, upload-time = "2026-09-12T15:12:13.855Z" },
+ { url = "https://files.pythonhosted.org/packages/11/71/a5ac64a62a04aebd556afadab14a6b730001e16df87266ded943a100a1d9/jiter-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e3f052c671d5f425cca5ea5901cf11a831369fba4a55a3862cab93c323b4c3b", size = 343138, upload-time = "2026-09-12T15:12:15.046Z" },
+ { url = "https://files.pythonhosted.org/packages/01/dd/f761e320ea473314cb68612bc6a435393464dbd198051399b36848b4ebf3/jiter-0.17.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785a216bbaf8f15fc974e964ced7322cd3d774bb0e86949edd78c6bffd6ba35b", size = 325805, upload-time = "2026-09-12T15:12:16.506Z" },
+ { url = "https://files.pythonhosted.org/packages/19/1a/27d8e40f0fb29bbc7a5adf30907144396a115dbe93d5d8976c054a6dfe96/jiter-0.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d85c558c9f8532bba287a990ac63767c7daf756f0d8c030219f62499b1fa228a", size = 340230, upload-time = "2026-09-12T15:12:17.682Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/c0/30bcde78a28155461f965d16b7aca4ffca6d17494d905f7a0bb072e6c64e/jiter-0.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c23849235d2142ce444b2b8c6eceee9f82f4cc0bd5c9081602e4155c6197807", size = 351343, upload-time = "2026-09-12T15:12:19.337Z" },
+ { url = "https://files.pythonhosted.org/packages/27/17/91420b156315ae22732f5ee1a7b5725a030aab9dc8fd7dcdacfb4aa588d3/jiter-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58df29268a95e910f17db7ec9178eb7f15aa8619aaca3575275c4e6b3f4fe4c5", size = 344990, upload-time = "2026-09-12T15:12:20.705Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a2/ae6d5672644cc11127970277c9aeb0fa6fae376845587f5b0a8e8828167c/jiter-0.17.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:a277f97eba7d66b1ee27eb5dab5b774ff46a10c78d89a1d3dcce04ce1357c8ca", size = 328624, upload-time = "2026-09-12T15:12:23.859Z" },
+ { url = "https://files.pythonhosted.org/packages/04/62/45cb1162f6aa586536e4a973fc339d72dc6b08cca030d70a838a307aa778/jiter-0.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe15ddf316f1f1f643347d3a474e74ce61880c79a11ec5dca53df20c071bd3e8", size = 334731, upload-time = "2026-09-12T15:12:25.229Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/5f/45c1574b644da7deda0b7591c349520dcf83ce45b24d7ca19922dab1fc27/jiter-0.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:02adebb7ce6413c44d40af9ad59d1c1cd79630ccdcb6f7bdd2d461e48c03d8f9", size = 483649, upload-time = "2026-09-12T15:12:27.557Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/d3/ebea1ecb5b241c519f192b30215c79a8e47f42f1621acbcd6f8830728416/jiter-0.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:55d0e0e613a3f9ad600cf436e0e2b8057d1b52bcf1d91b2d36ac53451231e6a8", size = 520758, upload-time = "2026-09-12T15:12:28.99Z" },
+ { url = "https://files.pythonhosted.org/packages/64/e6/682b641ff0765ea9bdc349dbc7d223de5c8af8ec1abda0db3406992f92fe/jiter-0.17.0-cp313-cp313-win32.whl", hash = "sha256:2c45ad7c973ef33fe5114a953377b35a95240f4542c0724d9f781e47dc24bac7", size = 184334, upload-time = "2026-09-12T15:12:30.813Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/d2/9a49aac2b27af4cc5015e368c0cc3588491a532f717a668ffce1f1ac57da/jiter-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:a3cebb1fe4a1abb00465f3f8a17e09112603e8b7c59e5c3adbcd9f7815a64acd", size = 226601, upload-time = "2026-09-12T15:12:32.096Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/ce/9a43e9f614608eafa78de22aedcff54cd21324467b5d442d5c9b00244145/jiter-0.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:96b8b0c6dc5d78682f54a450785e075aa929cde768304cad363cd4efba5a82ac", size = 183103, upload-time = "2026-09-12T15:12:34.396Z" },
+ { url = "https://files.pythonhosted.org/packages/01/9e/23065f8e2c7a4c372c1b6f6622e4cfab4dc786cb5150052b1527e6a6a840/jiter-0.17.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:00d783a779c5664e16dbad5e3a3c3a75e128b07dd5f4765159658d9210a50ca5", size = 292210, upload-time = "2026-09-12T15:12:35.613Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/81/67b58647560bc82a4490d722caa8561d7a86a9f45d4fa620b7e5fe282c7a/jiter-0.17.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0619d806e260ecf0c2a64521942c94af5d547c9ec99b55ae4f51b538b5576a76", size = 321512, upload-time = "2026-09-12T15:12:36.907Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/07/6658359a25f55927f7f8bf0e16465dee2ccd0b2a1a5208acc0df8972e074/jiter-0.17.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc0288ce39190ee33fe6e4ec73161eed34e7e2da509b525546ca061778d62b64", size = 343897, upload-time = "2026-09-12T15:12:38.189Z" },
+ { url = "https://files.pythonhosted.org/packages/46/04/5d50a9f0319cbdc37fd53c27f8c313d46afc34f1b048219ae6d8ea068da4/jiter-0.17.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a52a430d04225ffde633e6840bf2381d34c019ff98526b5929755b9052fb199", size = 326519, upload-time = "2026-09-12T15:12:39.532Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/c7/d02517832b29eb8275fdd0f4ce0f17b80f58cc4c3ebecd4d9ace990d633d/jiter-0.17.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37f33d327900bf2879613b3363fd48df97b4232d0c41f54bcf2e790c2fc40a71", size = 341369, upload-time = "2026-09-12T15:12:41.486Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/07/499b5f5603501cdd93a73a6a176dfad9c96555a3ae58ca9f8e3acba63dc9/jiter-0.17.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cf564d43c4388149ca58ee571d0f5ccf875e20d1fd4662fd94cc0d1ea3b10ef", size = 352160, upload-time = "2026-09-12T15:12:42.721Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/75/b04013c7743269d4533ef4e746fc0ed678a143968dd7448658e3f51daad2/jiter-0.17.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:523c499235fb65add25d4bb01b1c4709ce695efdc7deb6c0a7bc515b5c44e0fb", size = 345018, upload-time = "2026-09-12T15:12:44.192Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/96/cbb6fd1e42a77c8412ec4643db95059b30cdfc635e387cc9193e098ce268/jiter-0.17.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:455e4ab35cb2a4a91a8404e08fd3c621bae433922e59bf1c494fe20a426b013b", size = 329244, upload-time = "2026-09-12T15:12:45.491Z" },
+ { url = "https://files.pythonhosted.org/packages/15/67/d3be402f398566a379bf40ae65be5c3505b14d9e95e0802a597ddde7ddee/jiter-0.17.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6871973bfbd4408f7f1c632b30bbb5bbd9671c1bc8650af6823e24b7be13709b", size = 335693, upload-time = "2026-09-12T15:12:46.935Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/8d/98e2c4130b93d64f1d67c89060b928d04102549bf05e64451c9e6024f9ca/jiter-0.17.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:77f6aac0137309b31448c1bdcda4c6c77077664a6d018ece8d94019c68a5a5b9", size = 484329, upload-time = "2026-09-12T15:12:48.361Z" },
+ { url = "https://files.pythonhosted.org/packages/78/5e/8da91e49f0fbca37c3489fb4cf3ad6676d4965f00ae5468bca3a2513737a/jiter-0.17.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:93946d89fa04d5ba64dd323a8dd8d901676cb8a3c81d99ae4f6c051a9b4c3f2f", size = 521358, upload-time = "2026-09-12T15:12:49.856Z" },
+ { url = "https://files.pythonhosted.org/packages/be/21/5388684a5a38af3557cd9c2424b9827c71809cff24373c75ef9d0d3dfba9/jiter-0.17.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:70f19a2ca8429f91e82eeffb2f51cb87bc2d6e953b009b91a92d29c3a16ccb03", size = 110459, upload-time = "2026-09-12T15:12:51.747Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/ad/58b3a93525d2ffca7f54d9dee441381990082bd1172fbeb8d6a3f72a4dc3/jiter-0.17.0-cp314-cp314-win32.whl", hash = "sha256:71dbd74314c5df52a1bccf7b8bca46d14e943af7a2012e73b23f49977ef194c8", size = 185043, upload-time = "2026-09-12T15:12:54.477Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/4a/1aa520eb6c359b262c14ff995ca7283837208ddfb1202082ce9d73cf214d/jiter-0.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:ac3c6ee3264d6f5c44c617f90bc7e8b9e1587e7d6708c9d8f811cb65582ee312", size = 227163, upload-time = "2026-09-12T15:12:55.931Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e4/5997f648794bd9b499491d0ff480b096cc9a9c65bdba29f57568e6aa1705/jiter-0.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:6219adaf59711ba7063a52496e8ec6d3fa3e209d7827d83eee3b2abc780a1744", size = 183505, upload-time = "2026-09-12T15:12:58.196Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/4a/84a5ec271d09f7590b6073af5ee4abb44eab4ccace453b7e2c5ce45234ca/jiter-0.17.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:59bddbe6f9ffecc68d641e1e2d619ce64cf8a9e9eeb74e5c518f74fc87abf1b0", size = 321527, upload-time = "2026-09-12T15:12:59.394Z" },
+ { url = "https://files.pythonhosted.org/packages/39/71/9e1fd0045f5920b4c36be35c3f0f0dfd123668684f8ad352619d7aa44183/jiter-0.17.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cb41cd1432f1dc19a231cf70b54d42b2c9f05085155859263fce06fa4d41388", size = 340865, upload-time = "2026-09-12T15:13:00.756Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/2b/14627fd2bc377f3dd09491bcace6b90e34b4d7fea2f1f3295031ff91f528/jiter-0.17.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd7790aa79c8b518e512ebcdfce9f11d8ef5f30efd43720c8a19a548b39fa489", size = 325412, upload-time = "2026-09-12T15:13:02.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/f3/8d5808f7bf0f456bde79e6393587183a0cee5f83d179fe1f7f1eff2ba067/jiter-0.17.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbbfe4e3c21c8166980cddc5bee1a315df082454f007947dfb6fb73800768165", size = 340473, upload-time = "2026-09-12T15:13:03.485Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/da/1d8c7c6c4ae6b2423b94a81b6b907d37b28f87664e077427b531bf1b5313/jiter-0.17.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c286860abfe8b100cac1c02e225e5776eb9216edd71ba17cdb237da4af32bc9", size = 350757, upload-time = "2026-09-12T15:13:04.828Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/96/c1813dcca15c5a370145a448aaea7d1f83f6f0228a5f1130e79340ee385f/jiter-0.17.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f753eb70b1474a29e635e7542ff7312e6d6b951e0b25e8a2e8c34eeb1ddcd478", size = 345203, upload-time = "2026-09-12T15:13:06.131Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/f7/fc61cbcf2992d169ede13648fc3fd8e2d3171a3669dde43cd4db556549ac/jiter-0.17.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eae86b1f027031e39db2e0e9c4842221edb7b8cd474d23f87a79b3bd4b651768", size = 328322, upload-time = "2026-09-12T15:13:07.392Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/88/46418a3abbdffb7dc41b314200360f24f75faaeb35573e81c92de322cce9/jiter-0.17.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bf350452a43173e69e1fc74847c57a60e3d7515807287f29849baa2a85d8718", size = 336570, upload-time = "2026-09-12T15:13:08.666Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/28/b8a55b949be6306df8888e365a8df05441de8a7b11289f6957004302e41e/jiter-0.17.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:da139721f4b7cafdbff580a4f511ea24cb91f4909330c6b926a1ca53836c0a59", size = 482879, upload-time = "2026-09-12T15:13:10.037Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3b/21d0afa53ba0680962c39f3eb95ed2946f8793369ed44b0c82b490723081/jiter-0.17.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:8079849db9a1371bfd90bad088458a8fb836261879df2233cc9632464ecf64e1", size = 520406, upload-time = "2026-09-12T15:13:11.456Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/03/bcbaf8b6b9ea23c2c074411f8ecfbb02d820abac5d0cb8f4e280209174a2/jiter-0.17.0-cp314-cp314t-win32.whl", hash = "sha256:8f770b0c77e5fac482e1ba03ca1a7e18286bfb213d749932a00a7e4cd5de5e06", size = 184434, upload-time = "2026-09-12T15:13:13.037Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b5/5d6ce2c93ef6fe1241b37a9005547f9b6d58db1f07f39fe95807d4b98f51/jiter-0.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c4289293e5278d9314b00f15c37f2120fa51d3d68565292e715524c750e775a9", size = 227392, upload-time = "2026-09-12T15:13:14.933Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/4b/1e52baf90187606e33a7b8cfa8f96f5829acd7f01870077eb01059ab76d0/jiter-0.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4dfbfe5a6e1e80a7082af559f66386405025ec278833e0c649f69cbc6e1004cc", size = 182776, upload-time = "2026-09-12T15:13:16.239Z" },
+ { url = "https://files.pythonhosted.org/packages/05/fc/efe3ac75564ab10f53517958f5ccdc231fc7334af66c76776cb554a88967/jiter-0.17.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:84963d3f395ef5e9a32ce47155e08a7962fa292c159a10cb98b931cef1416925", size = 292143, upload-time = "2026-09-12T15:13:17.502Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/4c/46982118d91f9ffe9714319d21ec4f98d9b7e0cfd9062826c524a54de24e/jiter-0.17.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:ffa0380ad091de7d3fc33e17a97ff479851ee18a0a2a3ee56ff3215cdc886656", size = 321341, upload-time = "2026-09-12T15:13:19.133Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/12/9b1ac6ecc6307049913db54839ddba1c11c1ef72c5a8bbb5514bc3b50d1b/jiter-0.17.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:755079792868ce5d4938e83b91a0939b34fb858a1ca65a104f2d771bea57faa1", size = 344383, upload-time = "2026-09-12T15:13:20.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/b6/527cc72af836d824e9d4d666e64f0a1ca7eafd662a8da9657b78592172ba/jiter-0.17.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3bf4dc2b84a464117fb097d15a25c58d100d2692888e3b0d92df5b48ed16b7c0", size = 326841, upload-time = "2026-09-12T15:13:21.83Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/41/567f98617e88005b249503b933803f633ec6ba2d427cf4cc35e5c832125c/jiter-0.17.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02a360707033d8cef53f7f3480817a1489177a259ec6ec01e98c37e0b922ddca", size = 341354, upload-time = "2026-09-12T15:13:23.323Z" },
+ { url = "https://files.pythonhosted.org/packages/40/da/b29cda895b785f7d426e224638a885b6145a08ce853b381f34afe3e88c5d/jiter-0.17.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:300ce01ab0215e3dea4d00090143c909aedc65c0f809b3c07983e1d038f291b9", size = 351985, upload-time = "2026-09-12T15:13:26.526Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/5c/8a73829e7389e72ea298a450f2b3cb58e71a3e464b45f6d8753740f1c4f5/jiter-0.17.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746243a080b4ca790b8499af3d7cf9825d5f5987933950cd818e767ee353d826", size = 346052, upload-time = "2026-09-12T15:13:27.887Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/2f/98d6001026932c095ba440925570123043bed29f5ff56158dfe729a9e81b/jiter-0.17.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:b550585523339b71cb852b811aae49d08d7601ad8ffe9f5dc1562f4c3d22fd87", size = 329159, upload-time = "2026-09-12T15:13:31.569Z" },
+ { url = "https://files.pythonhosted.org/packages/94/2e/708dc1d2678f092c31c12754e860cd8353e6a85ecbdb1010157edca0da9e/jiter-0.17.0-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0239520085cac678e77a606fd7e3f1c60c371d719790c5e3807388d3da4354c2", size = 336001, upload-time = "2026-09-12T15:13:32.846Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f0/75a5ae38862f4eaf0fe2f8a9fbf6484c4890df04c06dcdffc45e36bca61a/jiter-0.17.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:eb2295da7c3769f6719b227a237aa6a5cfa6550e478bc838001b592c57e16575", size = 484281, upload-time = "2026-09-12T15:13:35.333Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/32/6636fae811c27c7f93e1b11fb5800de6a5c9e4269a27cf718e0b31218ad1/jiter-0.17.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:e088612ff90ebc9247e1a43074b72835804261c47e6a6c01cb3ddcb55360d688", size = 521300, upload-time = "2026-09-12T15:13:37.101Z" },
+ { url = "https://files.pythonhosted.org/packages/61/aa/12df7e0b0b1a2602e3d5a5a7104d7d9700f254b400f134a9b50955c4d231/jiter-0.17.0-cp315-cp315-win32.whl", hash = "sha256:0b52d52035b3907c5b1f6277857b29c1cbfc965e24e0f27330dbed83edb591ec", size = 185138, upload-time = "2026-09-12T15:13:38.901Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/ec/3dd2e495032cddde05723c1f4c743b67a23e55d2af244692a7f58f0cdae3/jiter-0.17.0-cp315-cp315-win_amd64.whl", hash = "sha256:10f5558eed511b830488003449d942bd75829ad6257dc58cb9a03e596a7777b1", size = 226950, upload-time = "2026-09-12T15:13:40.17Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c7/ef85704e0a57e9cadb2babc05f6d7c5df4a1c75da1a6ee31e1986b0099a5/jiter-0.17.0-cp315-cp315-win_arm64.whl", hash = "sha256:fa13acf1046f95df808c64b1310705e143fab87aee73ae00cc42d640867fd2c1", size = 183618, upload-time = "2026-09-12T15:13:41.432Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/9a/a4b348349de68762b58d6713973d363ad80a1c741d0bf8def7975f0ecb26/jiter-0.17.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:af2f7501580f274b63c4b2283bc425f5df7edf06ae5b171e5f87d912ff359a20", size = 321155, upload-time = "2026-09-12T15:13:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/70/aebd6d0b5f0677de3a3d0bdc4a05fac949b97c4ede454c8809f180ac7b17/jiter-0.17.0-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10c5349312e5cb02b7a21e123a57665afa895953f05bf252a9dd4c13a572b7ab", size = 340985, upload-time = "2026-09-12T15:13:44.115Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/82/4c3b49796b5eb62f3f5046f957683f4ba0135fe1a60957c11180512460df/jiter-0.17.0-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:86f3f9343a288eb85a81ef20a752b2f84564296636db54a9fff0b5c8deaf1df2", size = 325670, upload-time = "2026-09-12T15:13:45.901Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/43/f6341ecb4872202a4ef150486fcee0e1ace4aa3da39b71b82061452cdd3a/jiter-0.17.0-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4607ec7d93355fbc25b8dc5189153cf21d66063b9f9cd04dd2774e6e783f9b6a", size = 340339, upload-time = "2026-09-12T15:13:47.442Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c4/bc2c86e08fa065e03cb2fbc53b367c3640a7d257ef9d877b29118ea636b7/jiter-0.17.0-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10cd64a5720ad7f809ac5466ff1705813f1b6b510f195a73acafba0ac0e1f675", size = 350705, upload-time = "2026-09-12T15:13:48.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/67/91f12aa111cca6e3a197c3e36bf60a034bf9f122f6d41112a639e44217d8/jiter-0.17.0-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe9f61bb30174d2f5c8396445c360c96c44e78164d0815dfe627ccf57849574", size = 345011, upload-time = "2026-09-12T15:13:50.215Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/cb/9f5556e8f6ec89755fb5a709d8eb8270c9a324e31079eda0dfbeca451b6e/jiter-0.17.0-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:370d8fe5bf201dc6925e8a84c81ac7291f74d9fd1778234fc79d517064a5c76b", size = 328268, upload-time = "2026-09-12T15:13:51.809Z" },
+ { url = "https://files.pythonhosted.org/packages/22/98/153f20680fb75781a490fb849940e2b00f95035c7aa054df592f36ed33fc/jiter-0.17.0-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6b303d88e6a0bda789ec4b7801c7bad68e27230ba1fe4baffc756d1fbd32dc9d", size = 337024, upload-time = "2026-09-12T15:13:53.095Z" },
+ { url = "https://files.pythonhosted.org/packages/af/59/b16c9be3a5035df4466cc72e888188c027562de90a723d290ab6814cb9d4/jiter-0.17.0-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:30793a24a31e968969757c9e08d830cbb15a2cd3c4959b4498b38f4b1c2258eb", size = 482766, upload-time = "2026-09-12T15:13:55.713Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/55/667dea313094024bef082175d6bfe8976f90d1c00c926af9df1d8e0eab48/jiter-0.17.0-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:686c93d86f2b426c803024b805bd161a6cd10e9627c23e901640eab646c0ad8a", size = 520367, upload-time = "2026-09-12T15:13:57.674Z" },
+ { url = "https://files.pythonhosted.org/packages/21/e3/4b1a43501fb9ed17b01d137e380cb0e8fdcb39a254ce31aa2ab95bc861ac/jiter-0.17.0-cp315-cp315t-win32.whl", hash = "sha256:86d703d9faa1ffc8ae4e9de0fa007712ed2171b5c0d93811a8e2e105ac729b0d", size = 184603, upload-time = "2026-09-12T15:13:59.27Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/f2/b8ee0372b6ebdf1bde5cc44495d5291d17f961065f5b48f8616cc67cac2e/jiter-0.17.0-cp315-cp315t-win_amd64.whl", hash = "sha256:42b0260445251b1bc520a63baa94a32d88e0f931fba234f1764db7feb7c72174", size = 227936, upload-time = "2026-09-12T15:14:00.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/b4/923a1215daba959aed8355973315cb3f81f53e0d01c5b211870a27b41f45/jiter-0.17.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d47687806f9c54c84ea38733507081337922beca90ce819c7d852dd485bc0f23", size = 182977, upload-time = "2026-09-12T15:14:01.799Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/3b/05a917204413e2e09906dfa35240c1021227aeb56c7305abea9562c598b4/jiter-0.17.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:eaba834b72d573547b9d966465b3394b749d5e14208cc70acb63aca37619ab33", size = 288143, upload-time = "2026-09-12T15:14:02.998Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/e1/a1cd3c0cf8f79945939e4f8caae9990529f67d7f77f671769f73956329b9/jiter-0.17.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:51e1519d676a9f14dad9c2a411170d43b022ddb7989562df4e849b261ce127b2", size = 281847, upload-time = "2026-09-12T15:14:04.414Z" },
+ { url = "https://files.pythonhosted.org/packages/72/b4/9b797679e09f4a46c32986aeb3670bd9bc562fc0b373c7a0ee5c5dce1206/jiter-0.17.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ce4feb52493e3513335b2accdcd75605652e4632772d3c8c2f7b86954d7f39", size = 304797, upload-time = "2026-09-12T15:14:05.733Z" },
+ { url = "https://files.pythonhosted.org/packages/25/4a/0d77415b27a00d970e4e710f7c1de62e96a11c4cab3ed1add0015af04626/jiter-0.17.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29f49b325e0234e4ad9ecca5b861ffbd09b95ccac9bd46fa55841b6e56eea5fe", size = 307815, upload-time = "2026-09-12T15:14:07.105Z" },
+ { url = "https://files.pythonhosted.org/packages/17/31/4bb27f54333d3b9ef1e5bd3312dc0b4bbe59c68bb0885fdb40583a6b1567/jiter-0.17.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:454c4997d73cc466c71fd565d91e603b0274e48ea0c6b0b7a7aee6967e4ceb7c", size = 288415, upload-time = "2026-09-12T15:14:08.455Z" },
+ { url = "https://files.pythonhosted.org/packages/28/30/879570ecf82574eaea77c5eb10309f4b630dece5f2a556e9814a90ba3f2d/jiter-0.17.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:40d2c240f8f80b5b0f201b29f0ae129c81448c60c772227a41747b5e0026f6a2", size = 279113, upload-time = "2026-09-12T15:14:10.117Z" },
+ { url = "https://files.pythonhosted.org/packages/77/7a/1f0b8a35fbd079a4f1752c31a15dc99cf277f863747c459be0af39e900e5/jiter-0.17.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e05f5adbf68c4bd11e1610f394034d984152988e84be6f8314235ce6f2139e5", size = 303708, upload-time = "2026-09-12T15:14:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/8b/d76219ebdbcf3d4209d9d21a0810db4c8d0a6f88e3ee87d30bdea4e90d30/jiter-0.17.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c0bf24c72fd0491405dce5d40194f2070e9021ce648c1a1d46234b93d848ff", size = 307147, upload-time = "2026-09-12T15:14:12.897Z" },
+]
+
+[[package]]
+name = "jmespath"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
+]
+
+[[package]]
+name = "jsonschema"
+version = "4.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "jsonschema-specifications" },
+ { name = "referencing" },
+ { name = "rpds-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/iniconfig/2.3.0/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" },
+ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
+]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "referencing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
+]
+
+[[package]]
+name = "litellm"
+version = "1.106.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "boto3" },
+ { name = "click" },
+ { name = "fastuuid" },
+ { name = "filelock" },
+ { name = "httpx", extra = ["http2"] },
+ { name = "huggingface-hub" },
+ { name = "importlib-metadata" },
+ { name = "jinja2" },
+ { name = "jsonschema" },
+ { name = "openai" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "pydantic-settings" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "tiktoken" },
+ { name = "tokenizers" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/54/4b/0f42182e4a3f957e8b5d8bdc2522779092da973bef7a3492c8e6a3409764/litellm-1.106.2.tar.gz", hash = "sha256:9f0e770cb007c8c6e78917ad7b5c4f059f7bdc4a3606f854831780ea53fd9e2d", size = 21431422, upload-time = "2026-10-21T10:44:51.337Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/47/1038af80d9831972fae8712a890c30252f51c514c092d83e0f1330bb1fa1/litellm-1.106.2-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bf6f13445bb59d8d02131bbc1fb57161d1ae629bec65dc66f9add4c09fb6994a", size = 36283395, upload-time = "2026-10-21T10:44:22.902Z" },
+ { url = "https://files.pythonhosted.org/packages/51/fc/15e44b2a222dfda71e0aff107fccfb49b06a4695fae0eb6867aff84f7e1d/litellm-1.106.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:cc778d01ed65a8dd57d5211d581ec237e06df239a09c3d573ace33e0feea76c9", size = 35263059, upload-time = "2026-10-21T10:44:32.664Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/a8/f21d7fd8f7ffa6ebacefbedfb6268430e2b55ce9b2f33a36ac0295ccd500/litellm-1.106.2-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:34bfdc26c2c075726eda59ec0675e2a8a67b6e59a03980def7ef7651b849141a", size = 35827639, upload-time = "2026-10-21T10:44:35.553Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/30/4a38c22181ef7169b6c044b8294f2e5b06236aeeccfbe57c2f0e85a51bfe/litellm-1.106.2-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb1a2a1e55a4a8350ae2167525dc0d06f59b078cd685fb3e0f5419f02da5420e", size = 36789555, upload-time = "2026-10-21T10:44:38.879Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/76/e7ca210ad574f5321c162f2b27f8674c260e0857323a9991c4d3765ce8f1/litellm-1.106.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ce18971e2bb2aa58ef8efae1cd12da1248e1c2e5ae3cf5903bfb9d563bb34d9d", size = 35914468, upload-time = "2026-10-21T10:44:42.268Z" },
+ { url = "https://files.pythonhosted.org/packages/22/03/95518dfb58e29c45ab40a7bc3d1d76509d09011172c380145fc9e900a214/litellm-1.106.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:32e567ee53c812e36f79158ec280c8850973f84a9e5fdef50d31ca09306d148c", size = 36901860, upload-time = "2026-10-21T10:44:45.807Z" },
+ { url = "https://files.pythonhosted.org/packages/48/7a/a17de89184b5365980c3eb7d58176ae5a7e34538e56b942c833ff7f523b9/litellm-1.106.2-cp310-abi3-win_amd64.whl", hash = "sha256:836ab19110504ad7ba71d8ce73af6b9d1cf1dc6ffda2b49ebc1a51e348f754bc", size = 37619702, upload-time = "2026-10-21T10:44:48.564Z" },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/aa/9a65962e364bf19745f6bec7bde398fb1f5ca53ad2e734258e6198cc32ba/markupsafe-3.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd8ea6ebee7aedbf7c749fa80521d9ccf1ba473e0d1e14805caafbaad281c889", size = 11521, upload-time = "2026-10-02T23:04:20.141Z" },
+ { url = "https://files.pythonhosted.org/packages/14/36/927999a34b7d1def6957de89153d327fedc4030061940b5a468584e6c5a8/markupsafe-3.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dff05cb7016dff1e9fd68f4122c127b65dfc59de5306cfb7ad92f956f230bee2", size = 12066, upload-time = "2026-10-02T23:04:21.275Z" },
+ { url = "https://files.pythonhosted.org/packages/17/54/69e7b0db9bd687bfd83451eed5666384d67cfed3be062bfc59a06a15474e/markupsafe-3.0.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf63c214fe879a65e69a386f915e36104fc84254ab141240f8854602d8e0be2a", size = 22081, upload-time = "2026-10-02T23:04:22.221Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/14/f6f5c97903f7d2db76bbfaced31509a47d360a103b3aaf4849f6536591ec/markupsafe-3.0.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2a6ef68ae94aed8721934072b27a3b654ea2100b97e4ab864cf1489c90926fbc", size = 26079, upload-time = "2026-10-02T23:04:23.346Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/04/c3cc9b75f94f8b54d7e503c44cebd4b4a115ec1d6f1b996b999807daba99/markupsafe-3.0.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd9f8797427910198f95bced71ddfed61130d7e349213bfb8466c9c99e2c46a8", size = 22611, upload-time = "2026-10-02T23:04:24.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/26/c4708ed3b0f08e8e6d7cbce3314ac130cb5352d1f62951b6fef7878f2b1e/markupsafe-3.0.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1aca03ede943eb80ab3d63bb082c84b7aab85ea83bd0fd0c200260945fb49d9", size = 20712, upload-time = "2026-10-02T23:04:25.386Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b9/f3894d6aae3d7a52c9363317f4baf2fcadc052163d6dbc871266e32639ed/markupsafe-3.0.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0764a13d34cae40db7bbf3a09b7e9b491bf4603e20b263a7a9d6b8e324975d0a", size = 20742, upload-time = "2026-10-02T23:04:26.403Z" },
+ { url = "https://files.pythonhosted.org/packages/59/7c/8e248ddbfe286ab6bddb462bdac0851cbb1510d2cbbb815a415fcb0511af/markupsafe-3.0.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9388003072b95f2f1e3fd908604194d653ba21330d811961a78b7da1a77e9e36", size = 21560, upload-time = "2026-10-02T23:04:27.522Z" },
+ { url = "https://files.pythonhosted.org/packages/df/26/2353fef7d4bcf2b18e16bad81979fcecff2915156ca6882447917205b8e2/markupsafe-3.0.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8698d70a8081ee8c090dbb394768b5789a1da8b131b5499f89d071dd3cfaf6be", size = 20973, upload-time = "2026-10-02T23:04:28.573Z" },
+ { url = "https://files.pythonhosted.org/packages/67/6f/a9561d98d9a6ee3494b0b970a1c766e58bab128cc84841d56ec009456dbd/markupsafe-3.0.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf053da3c97a4bc5ecfbb218cdd2983febd91c617be8367d139882aa11e490aa", size = 22036, upload-time = "2026-10-02T23:04:29.568Z" },
+ { url = "https://files.pythonhosted.org/packages/55/83/6217df9192eca95af3ff0cad955c9854a93fa43b288cf7411ae24713eb52/markupsafe-3.0.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:9438a2648b2195980cb2dd8e53ed7b8df91319e2d0b70ae61a9e1d1bc8d3bec9", size = 20314, upload-time = "2026-10-02T23:04:30.881Z" },
+ { url = "https://files.pythonhosted.org/packages/35/7c/9cd8081dae70e17fdab3558121fa618d459c7950c8abd8fc10fc024486d7/markupsafe-3.0.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:88d59b473bfb03259722600839af9bbd7fa13a2eb514beefeedb95997882f69a", size = 20780, upload-time = "2026-10-02T23:04:31.975Z" },
+ { url = "https://files.pythonhosted.org/packages/44/37/c5f2f45f4d8f0c24e4af7b9dc711640c888044cbf5f7e2ba2c0bb74cbb55/markupsafe-3.0.4-cp310-cp310-win32.whl", hash = "sha256:4a540e2d3192792fc84eced57bef37851ccb2b41f73291bb17408eea77bcd278", size = 14096, upload-time = "2026-10-02T23:04:33.129Z" },
+ { url = "https://files.pythonhosted.org/packages/50/50/394a1c61c9b9972cb4f76875c332af7109bd39bdad09d3a0ec5327a339dc/markupsafe-3.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:5c22873ad1f0532ba40fa1727f3c0fc1bbbaab6d373d4cbe3f0dc74b2e2521c7", size = 14329, upload-time = "2026-10-02T23:04:34.168Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/9a/86d03d2f32ba41a57baa124a7bcb76d0a6d39b1622e9bd4d550074e8b61a/markupsafe-3.0.4-cp310-cp310-win_arm64.whl", hash = "sha256:3d23795802fc8bd72534836d64489bbf0f67c088959091bdb22e10735a5107bf", size = 14196, upload-time = "2026-10-02T23:04:35.201Z" },
+ { url = "https://files.pythonhosted.org/packages/32/55/18dbb4778b30ada5ce071608503cc3edc9e14e13d868c17a6d178fc30f7a/markupsafe-3.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e25feb9e330b63edb0278a0acdf85e50d0cb0fbf49c3084abbe4e24ae195346", size = 11520, upload-time = "2026-10-02T23:04:36.299Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/14/0b05f79b4733e264a18d08fe08fa1df7347630ff32a6cb82180d9dccec55/markupsafe-3.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3391b2188d18737cb2fa147028b1096236eaa7e156446c650a489fa2cadc91", size = 12063, upload-time = "2026-10-02T23:04:37.291Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/3a/63ba10b6c1463216b3e4df669a9f0e5a3b0c3071557d2e8229e3968c79fb/markupsafe-3.0.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:849dd2bb0e5e4ab2b71c7191726a4a8d5aa8a610daa584728cbee0b710ddc4ef", size = 24321, upload-time = "2026-10-02T23:04:38.262Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/2e/5f015261b76ad633d187ef6f388b413aedd64a8773c4df59e530a0be5525/markupsafe-3.0.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:befb4158af32106b9a93db8d6d1d1cbbd418c0d5aca0cabb7b1780abf0c89169", size = 28845, upload-time = "2026-10-02T23:04:39.295Z" },
+ { url = "https://files.pythonhosted.org/packages/33/cf/26e594b26be40c2f1fec63ccf8a8b99d0335a5b2cbe84835c7d82a994375/markupsafe-3.0.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71f88e749ea29f67f21f3b36433c1dc54c7729ed2a6d9e2da2e0d9e0d7b224eb", size = 24701, upload-time = "2026-10-02T23:04:40.348Z" },
+ { url = "https://files.pythonhosted.org/packages/81/a5/a513b76c139a3915b43404324e55c0b7979ae4f0d39eb6f075b0282e90a8/markupsafe-3.0.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6da83a088f8ef93b2d483a8232a4dbf4d69d3d8496b568a03c56becac43e1808", size = 22961, upload-time = "2026-10-02T23:04:41.372Z" },
+ { url = "https://files.pythonhosted.org/packages/46/cf/4c66192c100b4542bcbe392ae06696b670f66927be3ac38a213234778ff9/markupsafe-3.0.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f0fac8b13d14bb06c68195f849371924ae53dd7b1c00fed24650f704383b692", size = 21920, upload-time = "2026-10-02T23:04:42.429Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/17/ac3662678bfbad649893117ada2ba44dc30bf56884e84f13154a792b10f1/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4a7cdc2a420ca01058182da4253329764d4bfa055564d1eced90e6ba1e8b1d3d", size = 23726, upload-time = "2026-10-02T23:04:43.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/af/fe47cee339180a69ebca3c57fb3483d0f5cbd1e8337d1871fb1d9c1aebee/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:83b3944fea42a8400edf92fd1770fb8d0d4f7de651353bd2d8525a92dba69a21", size = 22539, upload-time = "2026-10-02T23:04:44.618Z" },
+ { url = "https://files.pythonhosted.org/packages/12/32/d55440ba140442800e02d799c9cb5ab597bf6ebdb1177b5ea39a11f797bd/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8138eb83940ec7299024d92d4dee45f601b9e6c5ffde9d25f4e35e326203c707", size = 23963, upload-time = "2026-10-02T23:04:45.656Z" },
+ { url = "https://files.pythonhosted.org/packages/50/9d/9c86042cb364c2ad4c971e6d1247929effd25f714cd7ee11b05e6316445b/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:811d02d5122171c1941357efd8f9bf4ffe907b7f0a1a4e729a880e4be3f46e3e", size = 21506, upload-time = "2026-10-02T23:04:46.671Z" },
+ { url = "https://files.pythonhosted.org/packages/75/ef/5b824f03ba40c3b3652b6272d083d2fc4fcdd440de519a3a39ba2c3e7262/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:50b5bedc9ed8a94fc8857a42ef4f84a81ea88f8d4f05dc8705fb23ee6d8dcca7", size = 22955, upload-time = "2026-10-02T23:04:47.698Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/e8/44cfcb5ea40e5e43cec7793ef90704ed0c475280839076c4345757eb8e59/markupsafe-3.0.4-cp311-cp311-win32.whl", hash = "sha256:2e5a7cd7fdd14fcb1ae5d7d8bf23d24fbd1daefd1fbca2580132e1ea75f098b5", size = 14094, upload-time = "2026-10-02T23:04:48.788Z" },
+ { url = "https://files.pythonhosted.org/packages/91/89/f2b509f7bf79352e40117824c1070dbeafd4df67031d3fa98165a3134228/markupsafe-3.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:fdb4ca07ab75ffadab4a8b135ad59cdbb3156b99310f3d565370da74a15d6bd3", size = 14328, upload-time = "2026-10-02T23:04:49.801Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/5a/ccf22672a0f64dc682306e288f0dabcb06c7a201f3bb6e6cbf86d9e8ad03/markupsafe-3.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:569d65055d367e3dcdf30c3f41119467b73d9ee9faf332bdf40402644f5ac08e", size = 14188, upload-time = "2026-10-02T23:04:50.847Z" },
+ { url = "https://files.pythonhosted.org/packages/81/09/4c59d56b8461ae8eb0d8ba34bb25b7e618547044679d58a82ef9b2479fc1/markupsafe-3.0.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61631e08084be9e21a8967ec3139c7616ed7c5e9368e05c86d1b39562c8a57b6", size = 11658, upload-time = "2026-10-02T23:04:51.876Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/f0/d6613774d86fbf6d145751d43c59875e47a6f9f17daee0aef173bd36d90e/markupsafe-3.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0930db9bdc62d22944e10b066448bb65dc9abe9112880c7cab8da54db4284d5f", size = 12050, upload-time = "2026-10-02T23:04:52.931Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/f2/8f18e0b806eb13c1f8d07d917a720831ead54253a6dec011fbc78098a6f8/markupsafe-3.0.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a45c3d514f2436064db00d7fc8778d888f0236ebfed649b53d13a59e69ad51b", size = 24363, upload-time = "2026-10-02T23:04:53.895Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ce/fa07dbe8a5675558fa36dea033e19995bc783de2dec5f540ccb9030b06aa/markupsafe-3.0.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1e1451fab512d1bcc3dc26988ec1edb0b82c2db909132872cd9356070a6b63df", size = 28525, upload-time = "2026-10-02T23:04:54.905Z" },
+ { url = "https://files.pythonhosted.org/packages/85/40/be87c01f3868ec217f8a2015089d71c22c8c5a75324822e5ed1cdd87210d/markupsafe-3.0.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bd3ce56ae2cbae3ba82b683bc425cd7e48d2ed8b10f3e818186b6f5646d9271c", size = 24733, upload-time = "2026-10-02T23:04:56.229Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/a7/aeedb5140afa41fc74c225e9184ab96723a6e873b6ee1c9fede7283456d8/markupsafe-3.0.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581", size = 22985, upload-time = "2026-10-02T23:04:57.521Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/fc/e91352bb08c6a59da3ef0909d457bf95a5f5908fbf151b30a06d9dbcfbb4/markupsafe-3.0.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c02e8f18bdedba082cef725942ac823b9b60656db07f7e265cb31618dfd00d77", size = 22001, upload-time = "2026-10-02T23:04:58.597Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/f8/bffee5e7d2a3deb59748a797650a48af7e672025cf641a79344a771ad106/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f098115c247e11d138ab83a28fa0323c77015007ea2df73ba5fd714dfefd67c", size = 23793, upload-time = "2026-10-02T23:04:59.686Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/59/b853d6628ecb4d658e1d637224846d5e9bb4adf4f8df97f3be9f29dce2ec/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d5f93ebbeb8032d47e349328ec8662d973d9b05a70b3c35df1f91fe419b84749", size = 22640, upload-time = "2026-10-02T23:05:00.768Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b2/1506df394f0f075797c418d0301498f49e43be194e3ffcb49e6fe6ccf022/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:64511c54db4e4987aef4c41923235927428729e8174c5dba488429be70a998ed", size = 24074, upload-time = "2026-10-02T23:05:01.813Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/81/5ed69cda630ac69ef60d06c09ba5a7f84ff66a2e28cf986fd5614ab3c6e6/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e1a622f13970d81f95d0c72f9dc090dce9085fccfa4c9f2174377ee32bd15786", size = 21563, upload-time = "2026-10-02T23:05:03.239Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/fe/fb1e79be0fea60aa32602ebefc9c35a82bb42b4df157285ab7dfec12341a/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e", size = 23048, upload-time = "2026-10-02T23:05:04.479Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/52/7632a53360671a9b750cdbabaf9cdd89f18b42248b8e4cb42c0b0296e459/markupsafe-3.0.4-cp312-cp312-win32.whl", hash = "sha256:f0ec3b750b59375eab5b0fb2b9254810c00a3375be6d789899f1055a1d556237", size = 14108, upload-time = "2026-10-02T23:05:05.513Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/bf/62495e180b7000aaf30000fff849e933f74264638057176cf46852500adc/markupsafe-3.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:11935df9bf455ed0c04eb87bcd720f02b1fe5e02128a9430f23aed6f93336fc7", size = 14303, upload-time = "2026-10-02T23:05:06.538Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/8e/4c24208776a65878d656996945aacfbfe010d3720d1a98fc0eb8491fc03b/markupsafe-3.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:a4bbd2d87dd233b9fc5812160c3d0ffbe42edc22a26ce0469f58479ede633fe9", size = 14174, upload-time = "2026-10-02T23:05:07.617Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" },
+ { url = "https://files.pythonhosted.org/packages/11/af/1a141081b905036ee904ec4bd945e1f70b4e1b32d33c4e59e8cf1d58b247/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96", size = 11583, upload-time = "2026-10-02T23:05:10.884Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/0a/a89385ae590232622a03e091805cff12f24fabe6c11e0e8bae096cece81c/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148", size = 11801, upload-time = "2026-10-02T23:05:11.913Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/85/ea548dc013962eb73653124bc595635fbf9e0fa41d1f181a967ccb784dfb/markupsafe-3.0.4-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e", size = 11443, upload-time = "2026-10-02T23:05:12.887Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/72/15f2e5ec9cf2eb00d5cdfe968d94e4156a7bd7303832c3f3b2c403a36839/markupsafe-3.0.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248", size = 11665, upload-time = "2026-10-02T23:05:13.829Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e0/4030bea613677e333c8a2c901fd405055f657f9d06acba5b7357984b6ef7/markupsafe-3.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72", size = 12061, upload-time = "2026-10-02T23:05:14.807Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/a5/28b76a7449eb702966b88bef599e2360b411fbb3afeee8fe560939be06ec/markupsafe-3.0.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2", size = 24395, upload-time = "2026-10-02T23:05:15.909Z" },
+ { url = "https://files.pythonhosted.org/packages/07/6c/21232811afc3a063b5e934b1ae2efda52f46154ec382f585149c020e61fe/markupsafe-3.0.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85", size = 27183, upload-time = "2026-10-02T23:05:16.976Z" },
+ { url = "https://files.pythonhosted.org/packages/14/38/6ccdfa5b59049cb36fb80cbc80aee9cf1fc9bb77d1335ad435f2070b08cf/markupsafe-3.0.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde", size = 24772, upload-time = "2026-10-02T23:05:18.209Z" },
+ { url = "https://files.pythonhosted.org/packages/63/e0/cec6865dfe88cb48fedd4b20aed6af5158e41092adcbf3e028bcc6ec2108/markupsafe-3.0.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6", size = 23017, upload-time = "2026-10-02T23:05:19.286Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/76/6ed4940bb7648a9aac457c14f870cfdd5105f139a0fb1f29cd61fafa47d1/markupsafe-3.0.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f", size = 22036, upload-time = "2026-10-02T23:05:20.352Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/4f/ed476226d4fe46a09090a36025bf319296810028df55eb12f1253b540f3a/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39", size = 23786, upload-time = "2026-10-02T23:05:21.576Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/35/66ff30450e35ef5fba9ebc930c9411747e537fd9447b65e44f5007e2b84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee", size = 22672, upload-time = "2026-10-02T23:05:22.922Z" },
+ { url = "https://files.pythonhosted.org/packages/32/0b/72f45ce4b4efcbca4b80cf1b06703eff0be8d37e82abb78f66c85a7ead1e/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2", size = 24107, upload-time = "2026-10-02T23:05:24.175Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/03/71776e5fdcba04614b384cc102e8a4198208579d896fd1394cb7cb9aa900/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46", size = 21615, upload-time = "2026-10-02T23:05:25.215Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/801ce02a02e7aee0f784b1ec7843026178f6adeb9c93ac67eb1992a9a84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17", size = 23076, upload-time = "2026-10-02T23:05:26.423Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/85/c43776625428f3bb4a61e8633940400e3efe6409e3c6f5bff26de5e45618/markupsafe-3.0.4-cp313-cp313-win32.whl", hash = "sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0", size = 14118, upload-time = "2026-10-02T23:05:27.716Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/36/163da64de88a13db79214ef75fa041be7fa13bdb42261cf5b7484de14bfb/markupsafe-3.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5", size = 14313, upload-time = "2026-10-02T23:05:28.749Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/a8/9b662783ffaa1149221432a923cee562f78b9cbbb8baa3df9b3753e63e1e/markupsafe-3.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc", size = 14189, upload-time = "2026-10-02T23:05:29.917Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/c3/a944f3b0df22bd129e96915b9f4e98d2eeca6516687d7618304a966c3c74/markupsafe-3.0.4-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:4ed644d75aa94a2baf7ec3a96eaa160ea58c742eb9d27c6506053c5c40fc84ed", size = 12787, upload-time = "2026-10-02T23:05:30.971Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/d6/a44863f69d88b6c7e27889108f70d47aed259edf89d5df3c5fca1eac87d6/markupsafe-3.0.4-cp314-cp314-android_24_x86_64.whl", hash = "sha256:6d2a9efe686f9de00d0d1ea32a4a5a86d558a2277501bd78d964214eab625e59", size = 12525, upload-time = "2026-10-02T23:05:32.263Z" },
+ { url = "https://files.pythonhosted.org/packages/17/8f/168ba80e532dd6a93f96f8f706f1ad41d7990b6e1aeedc1cc0d211a33497/markupsafe-3.0.4-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8781a792a070cf2bd1b86d3aa943894115faaba6e88122a7bf32d62072742453", size = 11589, upload-time = "2026-10-02T23:05:33.251Z" },
+ { url = "https://files.pythonhosted.org/packages/32/b3/aa2c95a574d3af39403a469b295886eb9b6d448da568cbebb5a2cbfdc2e5/markupsafe-3.0.4-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:971a3bbb75d97ae4e2e8f7d4834236f86f85f0c85e04ab2e191db1123b04f80b", size = 11801, upload-time = "2026-10-02T23:05:34.315Z" },
+ { url = "https://files.pythonhosted.org/packages/60/d0/34b810107d83840e768bf485de795893ebbae35b26ab061b487adfa0a692/markupsafe-3.0.4-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:8909c2f1c6dd65e054ac4b573a91c8384d1492281e55d82d159d653f7a13adf6", size = 11447, upload-time = "2026-10-02T23:05:35.302Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/ab/2f8488f0f817a39fca068d2b17daf446bf5cdb3eae28c3720af534d873b4/markupsafe-3.0.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4cf3468d5ec187ffffcaca8e61929a37448f215dafc1386a12c750a72fe53634", size = 11722, upload-time = "2026-10-02T23:05:36.363Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/40/e2d117b048d47282ade906fbfd92814cbee5647afc13fda88a3406039372/markupsafe-3.0.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:52704c5d36eb6dda8866493decd61111fff86244c9b1ad225ca01b9e91e5970f", size = 12061, upload-time = "2026-10-02T23:05:37.397Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a8/73a81135e85ba66217f5af7facb03bbb386807e1a729ab64532e4c802652/markupsafe-3.0.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1caa2fa5a6184fb233153b35f654e6687bd555476f6170f29d8ee9be1a8b0af9", size = 24434, upload-time = "2026-10-02T23:05:38.407Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/ca/fa9216dd01efee2dfdacafe7df32b4d0170fbac694b0c258a193d6e53999/markupsafe-3.0.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:387d8cd30e69b3f0a72877b9ae717033396404e19095b17fe89753a981fda44f", size = 27183, upload-time = "2026-10-02T23:05:39.581Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/4e/a469509e538d37af51103b17b073126973f2b1cbf197ff32c7ddf025cfe5/markupsafe-3.0.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:051417f74bcaaefa316276e0ff723f541616ca51043d070da00249d9bddd3e3c", size = 24813, upload-time = "2026-10-02T23:05:40.671Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/db/d7282caf7ab03af44d5d6fdbaa019b35c7d7f1c90588b839c07cba640d6a/markupsafe-3.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8e9f292fcda89b324f2f5c91d13f1424a153e40fc2756f38ee23b15835ff300", size = 23049, upload-time = "2026-10-02T23:05:41.864Z" },
+ { url = "https://files.pythonhosted.org/packages/30/f3/b6a425206e6964efda6acee544d0eb01d1501784d0b8e2dcc74986f33b17/markupsafe-3.0.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:df1ae86ff54725a01fa1a0510b914ca53a161b7050be74f6204e24aded5971d0", size = 22088, upload-time = "2026-10-02T23:05:43.014Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/8a/84d3582fc1f0d5bd466cdf2eebf175e172158a6e70701aacec1de1b35430/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8965520ac587c94a4ac48b729be3d8b8de00af39699b17585dfb599babe77977", size = 23846, upload-time = "2026-10-02T23:05:44.098Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/65/db101cce51b7ba4864ac491a9859d297dd1adf0e55b103fee9db9c47c527/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:340cbb1957ba99929cbf19a75626d36ba1ae21d1730b287d1cf7f824a20c4fc7", size = 22674, upload-time = "2026-10-02T23:05:45.23Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/49/ddee9813d71db0c7a5c9d97c832125e6758a0c844777f1cf076569bb0e22/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:3a93d9616ddecfb393727a0041a562cf0b15a244e20f2bd25efc7949be4c4f17", size = 24104, upload-time = "2026-10-02T23:05:46.398Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/0e/7d8518d726726870a2399d69fd30d0fa36c5e57a2132c336b58d7c491073/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d2e56fd3b00222722abfb3f5f0759ddbae4b90811b5ad4343c64030ad1bde70c", size = 21637, upload-time = "2026-10-02T23:05:47.48Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/b0/b505e8a361ba557dbf3b3aa7331ea39b00d2022a26e925ff8463b9714bb3/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0d9c47709875fdb321452056622e930c52afbc07a7d780762fbb8b4d91ce6fa4", size = 23081, upload-time = "2026-10-02T23:05:48.611Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ea/9cc3cea873f980c75cbdb6f4277ce30ee955de38be0b3d02f14c108e0698/markupsafe-3.0.4-cp314-cp314-win32.whl", hash = "sha256:38fc55594dab834470b6733dead2ee9e3f657fb0608c769dcafa0ba5ab52f45c", size = 14272, upload-time = "2026-10-02T23:05:49.707Z" },
+ { url = "https://files.pythonhosted.org/packages/80/f0/5792ff768a410f93ee3f84fc19345295ffc352d2c936b424cb37e514714c/markupsafe-3.0.4-cp314-cp314-win_amd64.whl", hash = "sha256:c1bc67752d5f21013cfe430df4062441714eab79f65a6a05e01505957e9c35fe", size = 14501, upload-time = "2026-10-02T23:05:50.788Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/cf/3d074a8edffcc6899355232ff2543ae8d929733239596423b7db79698bc9/markupsafe-3.0.4-cp314-cp314-win_arm64.whl", hash = "sha256:7e1636da3d8dfc220b6dd10264db5f2b165e4888c4518594898fbe381049af8a", size = 14364, upload-time = "2026-10-02T23:05:51.857Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/31/87ce42159aae2163cf3bbbd0c44bc87780510eecab1ea3859099aed95dcb/markupsafe-3.0.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:805c8b84534fa10891890f0e4be39f3a99e94615d93e8836bf9fa1fdca2feeb2", size = 11759, upload-time = "2026-10-02T23:05:52.951Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/53/b047207eeb7752e960aca3eb1df5fb7eefa7dd4c62ac49bb156456c8a702/markupsafe-3.0.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fa95848c929b6a75f6848d3c9793e59db365ee436776e57db835cdbfa79ba977", size = 12096, upload-time = "2026-10-02T23:05:54.066Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/51/4326c88a13c7b755657d44b4bb986f8c3d9843ecba7e22d98661d87f9a57/markupsafe-3.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e916035e3e9930cbdfdd10abf48861340221857f45509565898e012263f7b289", size = 25699, upload-time = "2026-10-02T23:05:55.15Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/bb/990581b7474bfcf2cf34bed6ba5ea23bd87adb9d671213d68e88620e7a6b/markupsafe-3.0.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b4d12837e0203bbace818ff4a7461afdcd78bcd782351cea148139180d7bcffe", size = 27804, upload-time = "2026-10-02T23:05:56.29Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/89/89491878c28e8291f5aa2fffe2c2d57230d10ae366d55dd810b840513d78/markupsafe-3.0.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5086f9975abb1ab531ee6afca1761e4b59a19b446f3f6522ed776963228cfe5a", size = 25912, upload-time = "2026-10-02T23:05:57.416Z" },
+ { url = "https://files.pythonhosted.org/packages/30/77/680998b54efdea06fc114565cd739b6d059f826a0279219b218dfa750d29/markupsafe-3.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4a635a0487774f841cb1fb62e907e7195cc95bc761e053184b8acc3ceb20733", size = 23623, upload-time = "2026-10-02T23:05:58.557Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/75/2709f5ac5de9467b40b10e2bb8f89cc63dfb74582e09aa734b1124a217de/markupsafe-3.0.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb96e6e088d6cf71c1ea977510948320234824cf226e32f6f6e044f7a9c82b34", size = 23301, upload-time = "2026-10-02T23:05:59.94Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/c8/39eadc6c5b14c9c7679bfb98f4d4c6a97863b5beb91839aca4d2d6e16e55/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8b5d563170ff8ba3181caa967c99a3c804d1dedb702c7cb93a6a7c32247da978", size = 24950, upload-time = "2026-10-02T23:06:01.289Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/5e/01037f8a43e8ccb0bffb4fbdc5212db05bf080fdd7286cd392332d58128a/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:396ec4e65cc889f69786b3b89478b471cee5a3bcf468b9d9bb03e1a30fb291fc", size = 23330, upload-time = "2026-10-02T23:06:02.441Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/f4/23e83ce0596bb0cbe670502d31df8f757bbd01a392aa486fa3b40d1ed399/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:15ba9e28640feef770374b116a6f019c21f52404aeabe516aa7f800587b98cfc", size = 25239, upload-time = "2026-10-02T23:06:03.579Z" },
+ { url = "https://files.pythonhosted.org/packages/88/5b/3708897368073cc683d524750474f41a77d2986152c380dcc55b20fdf340/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d920abdfa61279ba1a2ef9484aab07bf03331f8c08a10120fa332353d06e6932", size = 22767, upload-time = "2026-10-02T23:06:04.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/61/ebda1307864b409e6b3115757a3d4a09cca46cfb6cc65191b5de226b424b/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a9f54054101545a9a9cccefddf54316aa6e4491611fcbef9e91b3b6bebec04f6", size = 23668, upload-time = "2026-10-02T23:06:05.9Z" },
+ { url = "https://files.pythonhosted.org/packages/09/15/98075cceac3b5ba0dbb8e4762a847be967d2befc349a2cf2d0ac77f62c9d/markupsafe-3.0.4-cp314-cp314t-win32.whl", hash = "sha256:12a606a492de952afcb43b59a14aaaaad120e708d3663dd0fdf2d738d427a691", size = 14301, upload-time = "2026-10-02T23:06:07.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/a3/768b560fcc4156685cb563d922b217810cfa7bc135773367f62f1f9d2078/markupsafe-3.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a18f38cafc329bac5e3c2b96c765b4c96d3d103421ed22ab7988c1e3fce27464", size = 14526, upload-time = "2026-10-02T23:06:08.276Z" },
+ { url = "https://files.pythonhosted.org/packages/93/63/da554b4c97a6b0ea3229ca7fe8cbfb620be81613d517f482e85958550537/markupsafe-3.0.4-cp314-cp314t-win_arm64.whl", hash = "sha256:eba154571c16e032112afac0dc2dfe9e63c2ceb7aedd07bb7eecf2ce26d4dd4c", size = 14421, upload-time = "2026-10-02T23:06:09.402Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/30/54d11c8ca027114898cab97421fb39e4ffd9ddf47cdbc44df2ec76722da9/markupsafe-3.0.4-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:737c9c3981998eba27f11786f84fddcbabc74068b72a4a1f454ea02094b57b65", size = 12791, upload-time = "2026-10-02T23:06:10.485Z" },
+ { url = "https://files.pythonhosted.org/packages/10/6d/97c913e253a14bd3cd0e15a5c56d13203b823fa7ee32498342896a072dc4/markupsafe-3.0.4-cp315-cp315-android_24_x86_64.whl", hash = "sha256:489505b03f692c3f376394e49194fa7a7f9e8558d6e293a7056a0032b0c38163", size = 12527, upload-time = "2026-10-02T23:06:11.834Z" },
+ { url = "https://files.pythonhosted.org/packages/26/f9/b86d032042a4d597d9e1997f0e5f63a3eedaf11258e0a05760b0a0a826ea/markupsafe-3.0.4-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:077293e425f28ec737dbcad442a71752e28f8ae27cde3d68acd1fb212091cd92", size = 11602, upload-time = "2026-10-02T23:06:13.122Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/dc/73c14c1eedf0ac5fa3292ba43435e6c49d2c2050f33cebde541f8f4807f1/markupsafe-3.0.4-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9348cbb300d224fe3b89793262cb093504d4ae927004468463f745188a193e4a", size = 11817, upload-time = "2026-10-02T23:06:14.227Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/69/2c2fcaa5fcee22d72c7819c0d536fd181c74a688e6143845419579cd2863/markupsafe-3.0.4-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:b807e598953730f82e4eae3bd30f6a122cf6b31c398c6b504c0e04c13c170429", size = 11446, upload-time = "2026-10-02T23:06:15.574Z" },
+ { url = "https://files.pythonhosted.org/packages/88/54/9e5ec76c62e6e2834d5a93623018c943e8b3bb41d663e3fd4c03303b9b85/markupsafe-3.0.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:799c39bdf5e2f1292fedd3009f7b3c9e760f10b2420cb9638d56920840ff6db8", size = 11719, upload-time = "2026-10-02T23:06:16.701Z" },
+ { url = "https://files.pythonhosted.org/packages/96/24/3ec292b44064c16229e064d770b2625bd8ea941aa61f44905a9fa44942c0/markupsafe-3.0.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:ae9dcb8fbe244cb82f8a6458b455b927a03685e383d9bacf1ea5ce180b96dc97", size = 12075, upload-time = "2026-10-02T23:06:17.855Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/85/b64fdb1f304848518742136983c24e96d967bfb59a0ea160e92736901ab0/markupsafe-3.0.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bced6e2a6dba6a28f7dd3c6ce14df1b2dd495923f16ea484cad03decd463b2b", size = 24656, upload-time = "2026-10-02T23:06:18.963Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/18/23997d4c65b355da6390d61cd56e0ab3befd6ba8dda25cb40c602bd0fa6b/markupsafe-3.0.4-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3882fb412298575bae3b9c46868251f15cc69307359f87bb1b382e53d6e5a2c9", size = 27737, upload-time = "2026-10-02T23:06:20.117Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/36/35998dead3c6af88c38265a56e58100211f036234ab88eb2283fd4cbce44/markupsafe-3.0.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04e7902ba80ee4bac1d50a549606527a1dcf0476cd81403db41099d3b60ec653", size = 25003, upload-time = "2026-10-02T23:06:21.284Z" },
+ { url = "https://files.pythonhosted.org/packages/82/96/ef49135ce260db4ca4a12b119ed468449cd248db6b1468e2112b546d7a2e/markupsafe-3.0.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:925f929d6b59a8b3f8b8c6ac363cd0af7eecc81efb3071770b3c6717c450a369", size = 23111, upload-time = "2026-10-02T23:06:22.524Z" },
+ { url = "https://files.pythonhosted.org/packages/50/7d/83126e338bd88c17a220668235368ad719fd4638e426739858cbb8508f77/markupsafe-3.0.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f68edfc67aabac33708941f26f22a7b8e9f81429bc0cf249fcf7d66b23af8d19", size = 22403, upload-time = "2026-10-02T23:06:23.785Z" },
+ { url = "https://files.pythonhosted.org/packages/83/dd/daf7e420de23c8206c365204e7b85e1251d8e19d34196a56336f316e5ed2/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:e5c802729725bd07e2bc3ab7b76dc7e0bbfc53129d8f1eb1c002c24cf774717e", size = 24083, upload-time = "2026-10-02T23:06:25.037Z" },
+ { url = "https://files.pythonhosted.org/packages/19/3c/11eecdc06bc44ad5570350085b572ebf049e8f9a38d1ece6d76640b739cd/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:55ffd6ce583d97dc71dc92e930324c8c0d25aea7e3ade6ae54ef77cedb096811", size = 22931, upload-time = "2026-10-02T23:06:26.328Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/9e/ac0fd77f2a726e56ecc3ca0235d095feace1358d1b822406c2a2ef26a4dc/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:2cb3dd71fc6be918ad4264346a8ed69485f9b7ed7bf35495d8e22807cd6b8bea", size = 24299, upload-time = "2026-10-02T23:06:27.742Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/09/c6bd842ad58ff5b3bc76eeed7e9a42a6f11adc5d090ec697b72c9672731e/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:94f5407f7bc64fa6463906b896f9904beeeb7dd8dc116ee8e9056c8714ff9916", size = 21948, upload-time = "2026-10-02T23:06:29.274Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/46/82f586711fed61e86faa1ee1bc317d68cd45a10c8bdbe3f7d1fdf9026ad8/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2dad610540cb2e6272855c178f08ae9a1c7ac258a7fb71660553a5f104b42741", size = 23150, upload-time = "2026-10-02T23:06:30.583Z" },
+ { url = "https://files.pythonhosted.org/packages/19/2d/2dfdce99318abbfa26925195fbc17db188c46a1ec6457be121b6f9cfeb42/markupsafe-3.0.4-cp315-cp315-win32.whl", hash = "sha256:03470d1a8268e692ecf79ecd565593e59d44219377a7ead61f1f1b94c1f7ff6b", size = 14269, upload-time = "2026-10-02T23:06:31.949Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/ec/6000fd82e8791e58fcd0456ec20f098957e2b03d5ed02eb73241a577c0ba/markupsafe-3.0.4-cp315-cp315-win_amd64.whl", hash = "sha256:d882a373d8093c2941e01291b7ced96e9cbe4781da9a7751ca7e6c70385e5214", size = 14496, upload-time = "2026-10-02T23:06:33.258Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/66/e73bd5016421d5d6e2fb6de7dd609f9de020942ac8c626526bd8c6eeaf82/markupsafe-3.0.4-cp315-cp315-win_arm64.whl", hash = "sha256:353bd63081912ab8cfa6a0c7d185934cdf8426f04c618bba6bc4b394f2069b67", size = 14361, upload-time = "2026-10-02T23:06:34.539Z" },
+ { url = "https://files.pythonhosted.org/packages/90/df/cb8c3dc98d313a951df2f8968f44e4cb5643df6d3cab749a530ce2f7d972/markupsafe-3.0.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c61750fadcd119d0825bcb7d7d675dd264dcc89cc05292aab5be68ebdbb374ad", size = 11760, upload-time = "2026-10-02T23:06:35.807Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/bb/4af9b3ca0753d654ac75f9531d5bd741bb77ca6e696f36807c475ffc099a/markupsafe-3.0.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:1c0df495a977d10460a94941799c72d5b5ab03d3858d949b55b5a66c8f371c99", size = 12108, upload-time = "2026-10-02T23:06:37.089Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d4/b56429313aee5fd59b079c3df5615299959e25e7113eb6d8caadbdd7d38a/markupsafe-3.0.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02fa4acbc6a3fc5c693c34d4dd8c1130b7fe99cc915181b0ddd6f72aeb296002", size = 25762, upload-time = "2026-10-02T23:06:38.419Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f5/34c181e891aa4f7d59c918584672e0c5eb7fffe76c1387d1246008bf4081/markupsafe-3.0.4-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:05295589e619b9bed252a86b532b8e27350abc372d18ba89b59375325e91ec1e", size = 28400, upload-time = "2026-10-02T23:06:39.819Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/b5/ad14694fd0ac9a5ce30bc6498f2999378f418583dd1679cca5a1b512957e/markupsafe-3.0.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:be6cb0c799abb0e2ba3e618e6d28ddddf7e485f6c2ce938dfa237daf3905072c", size = 25946, upload-time = "2026-10-02T23:06:41.381Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/a8/26b606445387d0ceb1eb1f21840094b84e4e3c3c3983d80d10b89823b490/markupsafe-3.0.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26e9867520db70d37f7fb421a7f0d8adb40171011fb84ce869afa1a83370dfa8", size = 23634, upload-time = "2026-10-02T23:06:42.748Z" },
+ { url = "https://files.pythonhosted.org/packages/39/a2/b8814de672f1f0094d498bf646f2fec9d6356b503d28ef500b71c5095377/markupsafe-3.0.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f03460ff076f70ab595bb45a0205ccea1971443575b6920c52e755dec2b3fbfe", size = 23488, upload-time = "2026-10-02T23:06:44.176Z" },
+ { url = "https://files.pythonhosted.org/packages/db/c7/287223376fb73335a3cc5d6eb22c6ab01358cf33945a9c39c06b9dac3f4b/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:436e3ffc6310d3c41878c601db29098102fe5d8a467c49da4a4125254e0980f2", size = 25037, upload-time = "2026-10-02T23:06:45.646Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/29/4df8355e313426d19e62ba33e0253c009ca12a0894ee77d67fa67255361c/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:4e2c4809c14559aa7ef426f27fb35afbb38104c349a903bf8f3600456764bb38", size = 23543, upload-time = "2026-10-02T23:06:47.264Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e5/8377731e8495668dcc768f645e717df18318c841edaf023a99395f6da9b4/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:da2af0d7aebfc2074080d72efa6ab8317c62481ef1f896f65d9999c1c01f4494", size = 25282, upload-time = "2026-10-02T23:06:48.795Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/5f/373456e37ceb1478d657d6fe769cbe0a39f0a8dfc1548eeb19c471eefdd9/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:aa2c838cc024642cc04c6854232f32b43e5e22833dd11119c1766c7873b8370d", size = 22993, upload-time = "2026-10-02T23:06:50.31Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/93/2cbd5628435afb6f541bbaced4bce0c2edac4b09a142e6e928b8b0da9858/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894", size = 23712, upload-time = "2026-10-02T23:06:51.759Z" },
+ { url = "https://files.pythonhosted.org/packages/81/99/157e10966b033b363aeda5263e82596ee232a0b1d082fdbf90aa417ff083/markupsafe-3.0.4-cp315-cp315t-win32.whl", hash = "sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78", size = 14300, upload-time = "2026-10-02T23:06:53.241Z" },
+ { url = "https://files.pythonhosted.org/packages/33/05/55884815414c9706a23deca150b72c25a62109e65b0b6ce232077802c719/markupsafe-3.0.4-cp315-cp315t-win_amd64.whl", hash = "sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c", size = 14522, upload-time = "2026-10-02T23:06:54.729Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f9/ecbde7149e95b8a0f18e16d5d747f7dc06049d5da2e4f77f6f5e4a1f46a8/markupsafe-3.0.4-cp315-cp315t-win_arm64.whl", hash = "sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba", size = 14417, upload-time = "2026-10-02T23:06:56.246Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "7.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/79/84ddb5ba16c4eb2c69c71db76ae3c579fe546e511f7170c7e27eedbab7c1/multidict-7.1.0.tar.gz", hash = "sha256:61a4e5d81b8d4e4ad61964b230129e7a2b914793d96289029078fc9009f074ec", size = 362341, upload-time = "2026-10-09T20:31:38.279Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/27/77366104f779d883f1a13ef3e8e274cbc2adf75847afecdee0970ace37ed/multidict-7.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:24ad4921135a1410d95b1f1504f4901e1c64cea680014ce2c3c7a825f4f259fc", size = 166031, upload-time = "2026-10-09T13:58:50.692Z" },
+ { url = "https://files.pythonhosted.org/packages/02/c0/dd8df25b15560bd6dcb1d844150ed72af8fc2f2b52e29b4362b22c91383d/multidict-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b8429361241da973e594d15344a0989f44fd288ea58d33a6221fb7cc0daf27e", size = 95884, upload-time = "2026-10-09T13:58:52.034Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/31/e759ae1d3a44db8af1c6f37e1a0d290122b455ee4ebce4b989705b5a8787/multidict-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5c6455f2c11daeee40665c67494cedb426f67dba7375710524071c0c56d739a6", size = 99026, upload-time = "2026-10-09T13:58:53.365Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/ee/898d2a18bd1ef8b2adc20c7d8ab8f027f2b9e5da9cb40675133778e26215/multidict-7.1.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6120aab922bb3e15800b6655558cf8e0a5cc79518e954d457f064e5b3d5e9bf6", size = 436692, upload-time = "2026-10-09T13:58:54.57Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/c2/2a6994dff5c01685ab08db1a88bad2579fa1db3e74e568cba632118242f8/multidict-7.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14b1ce8579a43dfc0e592d93fb1d63dea693e4977980ac4166f26d494cc7a358", size = 455754, upload-time = "2026-10-09T13:58:56.159Z" },
+ { url = "https://files.pythonhosted.org/packages/48/27/353916a8c627067b54723792f172813025f87f9f70f9771c27214f748b01/multidict-7.1.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:71acdc6eded0f4b86b5e16c96314887cf2572a8eb5d8038b78583d0c0eb3aa1c", size = 427471, upload-time = "2026-10-09T13:58:57.569Z" },
+ { url = "https://files.pythonhosted.org/packages/62/f4/08993b1ee8e208dbf6cdcb5b536f4dd261211cde2c864cdcc2b527eb974b/multidict-7.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b7cd1cb0b363cd43ebf499beca26d201dd8b89eee49fae60205c82ba13ee03a", size = 477087, upload-time = "2026-10-09T13:58:58.879Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/02/31f6890cc54c83de25df712fab9300ef7b13edef463aa7e0f68e5afe40dc/multidict-7.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dd8a6b3e8f9edb07fe671b02d8c3241c8b641fecce7eb1e36432db3e55e243da", size = 488933, upload-time = "2026-10-09T13:59:00.632Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/9c/598d765e98aa02ff7204b6f11cf790e5840f5e8252d7b1d955ed0043bb94/multidict-7.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a8c826caeb7c08264e0a556df1267531c6ed90cc70506e7e5f4119e2d09f3d7", size = 464425, upload-time = "2026-10-09T13:59:01.955Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/07/b6b3508ba72bb82944f985cdd9c4d398c442c07270e5179ea03262b9bf03/multidict-7.1.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c44ced5e5168cdf677f0ae39900863bf2bda7d14a5e13502014005cfe040b8b4", size = 420397, upload-time = "2026-10-09T13:59:03.61Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/81/596582dfcf8d3c2d41ec03f24849be9e53c8a44aebd40cf45a31720112f5/multidict-7.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b57d4d7021bfd159db9f8f6f862a85a7a6027934643c512f028d6e5c60c4cbd2", size = 445983, upload-time = "2026-10-09T13:59:04.941Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/aa/600a68f29c2b027c2000e4ec6f51824790ff9a13a3828487716e31ac3277/multidict-7.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7fac4250b37d994e3fe42b46ba3c8bfa1614d1d7d8cf1cf23f303099082a9565", size = 421056, upload-time = "2026-10-09T13:59:06.469Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/82/006ef3fb5f2b798e9e538125667ba1b90ed8967bc862d7b9103297b2bcb7/multidict-7.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:966ae0588ac9959a040220063733b33f321d04eaf4e60349b42cd855d232202f", size = 436830, upload-time = "2026-10-09T13:59:07.882Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/6e/ffa1117db64d4be681f908cb7f38877c70cd06bbbdf29c8685839dcff7d2/multidict-7.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:50acd7ee7096949b04482cd7720cb6b85eb9cd9dd5d7ffb6704bfda250261a22", size = 465434, upload-time = "2026-10-09T13:59:09.272Z" },
+ { url = "https://files.pythonhosted.org/packages/44/8b/f3ab95a4fb591467c0d2a3c0876f38c17aa0b5c2cd60b4c53bc0c9867c7d/multidict-7.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ed6b7f402f3dabd1d72c798b96cf947005ddd796a5bea7b041bccbd517859a42", size = 417108, upload-time = "2026-10-09T13:59:10.595Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/2c/3e929884b0f3fd4ae96572d62952e18a76048115832835fb11075295590a/multidict-7.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fadcc96cd6155f35e6d85845fa4fcd37b35885dc8fda77b9f851cdfa538194c1", size = 471643, upload-time = "2026-10-09T13:59:12.082Z" },
+ { url = "https://files.pythonhosted.org/packages/32/7b/0c3451ada3a85c1232b04ed0209d5421b07ecad1f69d1b55939b3be0cef4/multidict-7.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c6b67f08014bfc4aedc22cf6a21010c2530cd5fbeb655730406827fe196296be", size = 459115, upload-time = "2026-10-09T13:59:13.613Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/d9/aa3b846e69c3730bd999f4f95943301ac26d999d9e285e75b9759c340a45/multidict-7.1.0-cp310-cp310-win32.whl", hash = "sha256:0604ff025497a050a2b2dcc4ae0e5cb6477c525e57b89825152c707e88d74d28", size = 81811, upload-time = "2026-10-09T13:59:14.941Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/2d/f4907e688463dedc1e644777851c0aaf2b814f37278436f9d3f4d3225a0e/multidict-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:36b14886aa3e0b8786ecdaa196374422c7b1c1dcc8764d02b2409f74d47914bc", size = 92126, upload-time = "2026-10-09T13:59:16.222Z" },
+ { url = "https://files.pythonhosted.org/packages/16/b8/5f438830119ab1979245c07bf61038813d2f7509f38d1031d6b8e2114bcc/multidict-7.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a2e575129c048bc286d696ed8e49ca148591768b2d77debcc6569f6fb64d0668", size = 92561, upload-time = "2026-10-09T13:59:17.47Z" },
+ { url = "https://files.pythonhosted.org/packages/29/72/c68c86c078a3b3cfa254bce218fbeaf1f6617d4ace811558154ae63d2c20/multidict-7.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:41ff3202cc23c800507777df5a4805b402f262b31008c60fdc652aeb6db2f278", size = 165442, upload-time = "2026-10-09T13:59:19.146Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/988c71ec3a6fe143b2971ac8d57cd5aca7cc6da970bccd1e4c2254997a09/multidict-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e50f7775b66c7802f4cb697e986c5acf30ec07301efee95b396c08114e890d67", size = 95611, upload-time = "2026-10-09T13:59:20.591Z" },
+ { url = "https://files.pythonhosted.org/packages/30/5e/7d18eb5a75cf4bacb6b3ee689929dcb27d7ed8ca2d245e80e9b8056fcbf5/multidict-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85cb3ced4fa84949cee12bfe78208b6ece7baf3cbd242b26dcaf773efff8d206", size = 98743, upload-time = "2026-10-09T13:59:21.841Z" },
+ { url = "https://files.pythonhosted.org/packages/72/38/2aeff3de6ddf235094aad6f833e3df14d1b3175e328748508443cb2b5458/multidict-7.1.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2128f3358335e0c83688ecb40c19d9d6606cd60784dfbf2e24e980ac2ba87b0d", size = 452832, upload-time = "2026-10-09T13:59:23.131Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/1f/94d8b7fc7d47bb43be1fc041bccc2784383d352258d4d0dd90ef55d1a2e5/multidict-7.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2e718fa9d1d900decbc240a533d5d0baf0947ef464c78a8cd4fa32b4e8f590c", size = 475378, upload-time = "2026-10-09T13:59:24.661Z" },
+ { url = "https://files.pythonhosted.org/packages/93/86/78f431ef2735fae7a773261ea26cff11e37cbbb4520bf3caed56ece006f1/multidict-7.1.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ecc68f5e47bc6f6f889bbed5bc657b22bb2237ad9ccab8229cb5a0d64f4cb536", size = 442847, upload-time = "2026-10-09T13:59:26.276Z" },
+ { url = "https://files.pythonhosted.org/packages/45/d3/1c5af82fe42f677f7c4dcaf487449511d754b84fc29f11a6d190cfb01836/multidict-7.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5f21fda91bd6c34455bd5c312e42aa1334da46cdafb4c533ecd01e0f7f19250b", size = 493652, upload-time = "2026-10-09T13:59:28.162Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/bc/414b4a83a12811329c046a7c1954d6f08a5eda63f8466939a6cb612da22b/multidict-7.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a90453a79423cd7145cc08fc92322dcd7aca4862258f533e03f473226d4b835", size = 506991, upload-time = "2026-10-09T13:59:29.701Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ac/a91709d7153489a3b53661b62af371112b96fb083d10443e22d54665a1e1/multidict-7.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c54ae1b89e582aa25f213cd8b5eac0bda1724e79299f486baeb3f562bbf82ca5", size = 480392, upload-time = "2026-10-09T13:59:31.179Z" },
+ { url = "https://files.pythonhosted.org/packages/72/63/a4fc53b6da419876f363fc6860f9fbaa48eb8066f458e907eb2989e9510b/multidict-7.1.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ff8dd079e7b5f3438332499233a2a5acfca0741fd0eb3d4ddba0c2d9bc04d19", size = 431248, upload-time = "2026-10-09T13:59:32.899Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/78/08af4a624b3b9d2f48af730c173394135d14afbf4b3de5ae6c80e0900fd1/multidict-7.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e4ef15d0a29fc2da67fe8ba2301ecabd6f8733696cc2bf0a0cf96a144a20328c", size = 463657, upload-time = "2026-10-09T13:59:34.721Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ef/06e5c75b88506972c880d228fe96702cf0fa058ea31f157f5fcd5efcf006/multidict-7.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5fa296f14068538fced53c6eec86520a2ef3d3d27a0fb134640d03e067986d5f", size = 436120, upload-time = "2026-10-09T13:59:36.135Z" },
+ { url = "https://files.pythonhosted.org/packages/23/8d/a917004a1e8ceb325c4cf0583e418a40b7ff9519cbfe759bc6982cd3b4c1/multidict-7.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6ab323f0c5490abaf35a78563e1043c7a772eb86d93f359ecc0fd286d1cd3807", size = 453859, upload-time = "2026-10-09T13:59:37.878Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/0b/d1cc417355625152b611790aa1f0bc780983ed8c0752f44087fc7dbe66c9/multidict-7.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:88ec4d16e9f58071c9896ea01c4da97cce9d01418fe844ff06eebb00e0a1386a", size = 481768, upload-time = "2026-10-09T13:59:39.39Z" },
+ { url = "https://files.pythonhosted.org/packages/94/0b/8e79adf65cf5430497d1020504b7d86ebe90e3289a95bb6b252a630d761f/multidict-7.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:9161eb81b8062da824426d3700d4b0d287f0cb0b05923713adfe3bd25e7937ac", size = 426973, upload-time = "2026-10-09T13:59:40.932Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/4c/e84d6e05943600b16cd2745587e65a407f89b3a4a1f77081d456c5a07011/multidict-7.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c564d0758748f38aec56a6b98c6801a427b3a63f39b7cac538b2b2d18ca32740", size = 489681, upload-time = "2026-10-09T13:59:42.663Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/98/01951236a01b3e15c037a4a796f329b5099374e46365d0bc65bb4a7b325d/multidict-7.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c5e4a362a95b85301d262ef6bed06cc8e4a144ac7e2be874cb4c3c46ae89d754", size = 475655, upload-time = "2026-10-09T13:59:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/72/52be318cdb46732da8eb118c2e4212fe570724eaef817901f8b702af2016/multidict-7.1.0-cp311-cp311-win32.whl", hash = "sha256:5d19bb1ec12e385c09215d5d53a243c060c7e8a0aacdba16d933e22902ee380d", size = 81719, upload-time = "2026-10-09T13:59:46.158Z" },
+ { url = "https://files.pythonhosted.org/packages/37/fd/1ed7b7d206ef7a6918ac3de515543ca4b49bc50131325ca1814c005daf3f/multidict-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:396ba9917fe489ec3a5942ae3e29e91324c8b9956f371f7e124c971c71379e7a", size = 93586, upload-time = "2026-10-09T13:59:47.618Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/1e/837877d218bfd2b656ca899ec9dd83e6eb82d8e1ef16e896d5abf3cb60cd/multidict-7.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:b5ed78742502b8d90ff2816688d407a097c8b5cc6af4343fc5ad7a98df53a7cd", size = 92446, upload-time = "2026-10-09T13:59:48.996Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e1/215e7df354e2907f3af9d910c8136653cfec94796012a776359bc802a326/multidict-7.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ccfb950359a80de0fcd2030ad60ac1b1a861462de3e2ef746697c9256659af21", size = 165426, upload-time = "2026-10-09T13:59:50.564Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ec/461ba588b308ada2cd16907d6ea425ca4d417596b88c12814ad9a0bb7325/multidict-7.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:939d8cd2d8c35e3956f6bc858390b6ccb611e6152b4920d64ab5e98f3fcf39e4", size = 95943, upload-time = "2026-10-09T13:59:52.381Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/84/31444ef07ec13a33c42c2772d986129e137e69c4beb89ca64f2138988145/multidict-7.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f8e95c95039eab6a2dad8c83c38ab87fc5431d28849e0c8a7e2a4e70ba38710d", size = 98294, upload-time = "2026-10-09T13:59:54.066Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/10/aca13806d73d88b5b01e35e828e23a346cb1abad710a49dff0507702efc9/multidict-7.1.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:05d12b4bac53abe0c65f3163af2b45894e2e1c0cc55493ac784d52a350047d88", size = 453301, upload-time = "2026-10-09T13:59:55.397Z" },
+ { url = "https://files.pythonhosted.org/packages/96/8c/382d771bfb3a9282d98332d0e1f27fd1f8b4ae0e7175bd7e008ebf934900/multidict-7.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e79ed92b1dece6bb57e9b46effd74d7a5d3d00187c85466d880ed184239a698", size = 478570, upload-time = "2026-10-09T13:59:56.899Z" },
+ { url = "https://files.pythonhosted.org/packages/52/9c/e81b0c92449da3a1950a575c520d957d7be618777d170717a71e00558d7f/multidict-7.1.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fab380fcff8b3555eb2bd04304fa4330909a771a9a9b0dc07666cfc23148a711", size = 443699, upload-time = "2026-10-09T13:59:58.465Z" },
+ { url = "https://files.pythonhosted.org/packages/63/72/f8f5fee6960d1b580a7b046c7c5abccf67aa26fa5647927a91c9c835c2f8/multidict-7.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b5c41e44da74383c924cc5d75ef0a268f301d69305b3c42bd17af685d55e412", size = 497332, upload-time = "2026-10-09T14:08:01.88Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/92/a25d7db3ca451b588e743e067ee80f2b475edf6467a56908454faf6a714c/multidict-7.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dbaa7f7c2f0ca8578895fc61fb8c8e50ebb405dad8982f92f4343285c7a3fda", size = 512457, upload-time = "2026-10-09T14:08:25.465Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/f3/374c0ab122bb98b1a62a8742b1e3f59563e4d609942005a4041861a0df67/multidict-7.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed6b7705d49dd07e5e0dd5f5c873fc44047e92d714299b13245b5fecac49d01", size = 483000, upload-time = "2026-10-09T14:08:27.132Z" },
+ { url = "https://files.pythonhosted.org/packages/46/1f/01c8522859771dc3cee840d5852233fe84c91aa982a1cd8ad594306d25cc/multidict-7.1.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53daa47dd176db64bb35170e3d5d0ae2388c060121201883696278f055a0e70c", size = 430746, upload-time = "2026-10-09T14:08:28.715Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/f3/af90affc8cca6b4ee59b2ec354a20839005e829d14d155451009810ef1ce/multidict-7.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:542429c796430de924d03b68a6173bb6d79d5c4967d4e9a18de3e501cad55593", size = 466859, upload-time = "2026-10-09T20:27:24.139Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/97/1b6762f37f6331449e164af9d5500f0abb0f23670e81ba543441e0a6be1d/multidict-7.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c44ca6d3cdf4cfcbcd4f928fdcbe87af5fd7319f6ad4169617b7fd6b4527c33c", size = 441952, upload-time = "2026-10-09T20:27:26.559Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d2/4908177fbf22438799c04ba11a2853a99c69d028fccefe61f19e68caba0c/multidict-7.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eb0228c809b2e7eb47921876050af0bc4214b351bad8d8112f70b6ed4288763c", size = 452810, upload-time = "2026-10-09T20:27:28.347Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/05/5031f44f680ec54fc182c4d71c9ab7c07216983946aa64ce6fdd56a52692/multidict-7.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6ad60de1f4c702448fc8f1449f05e810f6b7957c08a5b3950c8a792dfb13b50a", size = 483934, upload-time = "2026-10-09T20:27:30.14Z" },
+ { url = "https://files.pythonhosted.org/packages/da/3b/9b21d107dbe96fa7e6966ff9e5e10b9ac0d2d1c2cf1633098e4c700f865e/multidict-7.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f79def86aee67b5ba01b2565f1610f262bf88ae53c379f93e5fa29c50fe793be", size = 427042, upload-time = "2026-10-09T20:27:32.433Z" },
+ { url = "https://files.pythonhosted.org/packages/53/ce/5b01b1041580072866b30e39c6380bff269e72fb5ca41a7f2fb828ede943/multidict-7.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:248dabb89b5aa90b2f7e43e045f048f7e5392ec77b6446d80853ba7117d7bbdf", size = 492543, upload-time = "2026-10-09T20:27:34.459Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/6f/6508a23fcc7b1122e4f18409d7900ffeb3cd020cd080fe98aba3eabf9486/multidict-7.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0747a83e7ae617793181a4763ee8b84863cec5c0bbbde70c4394e4c0276c36de", size = 475796, upload-time = "2026-10-09T20:27:36.289Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b6/ebb6433f4aa55ac1fea4bf11e860e99611640d07cc92d21cb3f35608de22/multidict-7.1.0-cp312-cp312-win32.whl", hash = "sha256:1df055e51fe7491120cc84f3362bd43db186be78d0e4c476acad45e435af9ffb", size = 81250, upload-time = "2026-10-09T20:27:38.229Z" },
+ { url = "https://files.pythonhosted.org/packages/61/0a/11240e5e7d2e986e288f4a6090f569ad00225b7c2c513d4096b36643f9ab/multidict-7.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:10202ba98cfb3f7eb60da7ca87a2c458a69b7d0d6e4d4388cd6773ebbce89085", size = 93817, upload-time = "2026-10-09T20:27:39.739Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9b/a04ffd76db7cca2a60d347e839315e1ada63017c6c343fa90a23a5a55433/multidict-7.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0aa1ba3ff7cdda05a1242490612976b2ae1c90fc6200903ef8f53815dcb35c5d", size = 92312, upload-time = "2026-10-09T20:27:41.285Z" },
+ { url = "https://files.pythonhosted.org/packages/60/34/22ee5f60d704e8edfe6e0a37ca1e2d1efe276560dc8f6f8caee35ea7a4f5/multidict-7.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:51d7f33be9a4a1a2801430846d72841deea0894eae8381a07e7d90e0f71b3c4b", size = 101454, upload-time = "2026-10-09T20:27:43.102Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/6d/7652943397171fd624de163d25fc6d2807852931c40c24776cfee3f857cc/multidict-7.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2a964dfeb2aba3663f0536c809aa1ff385f065e89fae57e883fb7edfb4067c2f", size = 95823, upload-time = "2026-10-09T20:27:44.912Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/57/717d1aa4e901f58a7e93b945ed741c860d950f456b75be47123fef507ac2/multidict-7.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d02cd23b5af182a49d635ee72be38053767711987a9fd82625b16b93828a0d8c", size = 97841, upload-time = "2026-10-09T20:27:46.616Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/74/d2d22d306225f3c53ea5a682abb8a43bc30057d6c78b5c94a74035450bfd/multidict-7.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0179698c3c913eb64f32397083747fad20ed0f0a2b7469a08cd1a8a95d14d90e", size = 164661, upload-time = "2026-10-09T20:27:48.495Z" },
+ { url = "https://files.pythonhosted.org/packages/55/f4/1e63fea41ba86768e18dbc1743e6780f75ad32d7a45e418740c6fb64589e/multidict-7.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ccf98ee859fe29f874ddd8e637f14ba59108a333492b521acb885a9095244a9c", size = 95489, upload-time = "2026-10-09T20:27:50.055Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/55/477c351b21b34fe948ffffa8b64cd0c246efd998fed3de922b217e632e59/multidict-7.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5fa1484f74d011addf2e5f5a0378ec41521989839a05d6051d8067d8ce732423", size = 97931, upload-time = "2026-10-09T20:27:51.598Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/dd/288508d7deb9489dd7c8e0b172d62dc1da8f3cb24877293ead42e6cc2047/multidict-7.1.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1fed3d721f75c25a9fcdd0e362af53f4b20acbcdc63081112f85419ba0ce3444", size = 453816, upload-time = "2026-10-09T20:27:53.217Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ed/172447dd09f06111f69d2cf22a018020b34a93a39ddf72cddfdb48323449/multidict-7.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df03e392cae1e05462918abbae06d6100f1e53f67db971ff0ac6c07d9edf7321", size = 477779, upload-time = "2026-10-09T20:27:54.972Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/7d/e5b7755e84611ee846f0dc830cb1363ce10764b29247a01b857722fda653/multidict-7.1.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:379f477b98a1e9a77ddc3ccaa8c709d3fb4a288ff54b96e171e637b55b4adbae", size = 441299, upload-time = "2026-10-09T20:27:56.901Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/d6/e9c93da1644491610f09258349283421a279fdb3b383929b4d963698691c/multidict-7.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:40f586bc8a084a3671ddcae9e5fbd3228a596bfb63d9f0380f153f9a65b69f08", size = 496193, upload-time = "2026-10-09T20:27:58.788Z" },
+ { url = "https://files.pythonhosted.org/packages/22/49/7fe19efed1b1c73e2f6ae65eba4e6528eeb26e10c970dc0bb0fc009a2aae/multidict-7.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6e7f70d912a589e30290ed926f90ddbc3160998359cbad7c9ede1bcee481748c", size = 511469, upload-time = "2026-10-09T20:28:00.689Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/2e/7b708a72001323dc6b2930834d63e355870ea5b1ee8d1450e0fe1a24752b/multidict-7.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5129cc1f5fec6888e2db0be936dab67242e32c738811c8769aeea93aab4257a8", size = 481377, upload-time = "2026-10-09T20:28:03.011Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/60/3d23e7d2ebc7e7e6b1b205de0d5c2ce0652a9b2edef65eea3528758b5699/multidict-7.1.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1401caec21fd7f002e79ab6806bbfd1f54bb3de6d5e12bd91c6685dce16ad2be", size = 429992, upload-time = "2026-10-09T20:28:04.846Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/e6/8e3bef78ea1929a3f2c395aa292799d4effd6ce1b5cfce42c1986a33e2b5/multidict-7.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c81062e947f4b5a624135a843f6ac4b3c7fe6508300c9fb27347f022ba0c513d", size = 465635, upload-time = "2026-10-09T20:28:06.527Z" },
+ { url = "https://files.pythonhosted.org/packages/44/6a/55fe5dd90c0e9ce60f5c4fa8ace27eeb1190d9cbb151929b04412f0c673a/multidict-7.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c53be0dd676484a660acc56e4f1cd0dd74bc1255d12fa285e86a3fa9d5f22bf9", size = 441693, upload-time = "2026-10-09T20:28:08.443Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a9/628913f71537dce7dbd6c4ee1ae5019976264427c55354ecc593b4b921b9/multidict-7.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:41e0c3350d08994ee8640c39884e16514e282f70ba40f5b2299582509a327774", size = 453808, upload-time = "2026-10-09T20:28:10.871Z" },
+ { url = "https://files.pythonhosted.org/packages/25/d0/3ae3af653b5776d045f460582006d0b4d7dd41b078b3ab2e874bf4d1e22f/multidict-7.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:abeec7a89d698aa1c9b4c36bd5e3c746faef0867076e6a2ca27fa5077c4ece26", size = 483160, upload-time = "2026-10-09T20:28:12.892Z" },
+ { url = "https://files.pythonhosted.org/packages/56/3e/2c13e111b9f4c6216aee0ac57ac39c53db7f97e42bb8490e60a06eaca352/multidict-7.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:23f6d325241b0db006ca2841309ed17622137e134930a740a8f1331ec4404791", size = 425896, upload-time = "2026-10-09T20:28:14.65Z" },
+ { url = "https://files.pythonhosted.org/packages/45/13/15b4d614cf0d6838a7d89eb14f4820fdecfd1b7f529061d172a674912d41/multidict-7.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:034b0dc1b7fb8279599c5d8563f86abb4d2454735b06544ecab23c54572ad2bd", size = 491674, upload-time = "2026-10-09T20:28:16.508Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/97/3a6f8c75f12a607ff047e3db9a45dff6a558b1e31b89b2f0f27da0fedb6f/multidict-7.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c8074ad4d67067c87bd0663dfda654f786336078c8fd7d2f6c1aa41de8494cc", size = 473989, upload-time = "2026-10-09T20:28:18.284Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/20/5e7726631e50b6afd26324a234851a06104b8ee30f01bc209140788b76c6/multidict-7.1.0-cp313-cp313-win32.whl", hash = "sha256:7b25c335fc53acf29d4d21dbc19fe39d2824201cdda0448623152cc5917bd259", size = 80952, upload-time = "2026-10-09T20:28:20.044Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/19/b26bdbef5bf5071ea5a3ba37c963af187fddb8b0a925bd39c0c5dd0ad04a/multidict-7.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:7de54b49e6da811b0321e412d14efdaa1ee0c0b6609296ea5b9022bc5b2bd843", size = 93554, upload-time = "2026-10-09T20:28:21.657Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d5/e9a8600d3868e3fdc4a4be1d15e594048508511c0398423df0ed18c11b5a/multidict-7.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:aeba2c750102051aa51e087c2ccbc79f2724a41c94168f8731e36f54c453551a", size = 92039, upload-time = "2026-10-09T20:28:23.464Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/a7/c9f5c08348a6f903143b631546e22becf1b704a1304b17f7e1b9850308d2/multidict-7.1.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:128ea4142f81a79d430f3d0eb55206093e5eda03a12abbc7b03c34748ff6116b", size = 100435, upload-time = "2026-10-09T20:28:26.111Z" },
+ { url = "https://files.pythonhosted.org/packages/82/60/92fe617008c74cc019483b1c59202c48738c6f637ad5b12b36f01e21db43/multidict-7.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:439a19f7fbbff232ce96682c57e27030b8ac3a4b8121484c94f04bf99d08bfff", size = 95267, upload-time = "2026-10-09T20:28:27.856Z" },
+ { url = "https://files.pythonhosted.org/packages/17/78/82182f311d673f17de15bc7f7465c7ea5cbd2987ca3a6bf6546fa721e9d8/multidict-7.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8090c35199d6b7bc6426bb8bdaf341e64f295cc2624a1fda7860c0837f1acc03", size = 97233, upload-time = "2026-10-09T20:28:29.817Z" },
+ { url = "https://files.pythonhosted.org/packages/63/25/af4e482d053fd73b4b1d60de3ff5578c6cb1d319d39d3f2a454d65409835/multidict-7.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:b7cc5333fcbfb27327d12612ed72322f221b61c2b69deb1155078c964f86e1a1", size = 163070, upload-time = "2026-10-09T20:28:31.495Z" },
+ { url = "https://files.pythonhosted.org/packages/df/d5/eaed52e199451dac445305fb2631d3f4e7ac9536aeef01f23622a1d93f90/multidict-7.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b0e0040b0d8dd89bd0af9ab18901981e344ffba68bb30b8eabb4eab6c303279b", size = 94582, upload-time = "2026-10-09T20:28:33.263Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/e3/ff58ecad5161baa98dec05716e2745449446f2424a9c727464bf452f7fd1/multidict-7.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:33376418ab2846b931a72b36cfa16810befc4f49485d0b3f4dc054a4d6d00038", size = 97406, upload-time = "2026-10-09T20:28:34.981Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/a2/ae4eadf02d4bc035baa7cadf5548ca4fa441517193f76ce1aeb5ed276ae3/multidict-7.1.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1101aea5c3eb1d26e090b931c693488af0db9f3d52e68be8d4cdd807dad9841d", size = 449908, upload-time = "2026-10-09T20:28:36.659Z" },
+ { url = "https://files.pythonhosted.org/packages/91/55/bd5101ef760d1af4c3f246330b29b48ee22bfdd5a83150b713dd93b431e2/multidict-7.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f996b19ac89e0dae65821ce65f788619e4286f78c62d005ecd3b75b5d9c0892b", size = 471605, upload-time = "2026-10-09T20:28:38.561Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/8f/3dea8a28b0416ab71d47c8ce274bb3cacedde2d9838647ac67cdaa3d48e6/multidict-7.1.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e0d91a4bcb59ac0d7af0d8e0da737332e1b7fe6831e53e47819b1b5349d431b2", size = 433523, upload-time = "2026-10-09T20:28:40.913Z" },
+ { url = "https://files.pythonhosted.org/packages/24/97/805d2aa4f746cc43cf4b206f1b1bfe2566add95bbb145abd3d9cf61858bf/multidict-7.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e96d67914ddbf5466e4476a1cd7ff30a332cbab85ed895207acc3e58c979b6a7", size = 488277, upload-time = "2026-10-09T20:28:42.851Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/7b/eac247b7e76f6010071c7c2401da5255eeffd1b2ac981925e533413d21ec/multidict-7.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:170ba61761f59ab92afcc86ce5534a3f3d0b07c38b339b950a83213f22dd86ec", size = 501888, upload-time = "2026-10-09T20:28:44.75Z" },
+ { url = "https://files.pythonhosted.org/packages/87/58/de62e27b09dd15756265765945f68f1c0d1e23eaba09e2e109fbb4112425/multidict-7.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:33389fe084e5426d9fd85d7d9ca91a29cd0d88a83c7c96e411aca49a3f9967bc", size = 474883, upload-time = "2026-10-09T20:28:46.704Z" },
+ { url = "https://files.pythonhosted.org/packages/21/fd/afb4e50ce3b44707b5ee39c6b5fa1573bdb50c4be660bb35b040362b5170/multidict-7.1.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77024596b9046572c4e90b34c1ff212346756dc48933f90c53cf6e233660788d", size = 424327, upload-time = "2026-10-09T20:28:48.681Z" },
+ { url = "https://files.pythonhosted.org/packages/23/70/96c9abf933c4edae53b8a1c8d3f038120a3a60d363f16c6d94eee9b04851/multidict-7.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ff15531a376dc6f35984443fd1429e4b150c36ce27633e7cc52a9e5318546e20", size = 459255, upload-time = "2026-10-09T20:28:50.562Z" },
+ { url = "https://files.pythonhosted.org/packages/24/37/dbc1dba26dc1c9e42aa07ee01908131a67fee0e79ebe5c1d0e7fc00aad55/multidict-7.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:544f2642a456fa264614e975d921540ee8c3b368b04d5aa1ddbec33241b13e08", size = 432476, upload-time = "2026-10-09T20:28:52.361Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/0a/eac70cc1461668bad8253a2da727670a56293eaba112aeb4079af8cd37d9/multidict-7.1.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:08834fb8b20e1a985c70e8380a10940234b4162de62694458727330376e58b33", size = 450030, upload-time = "2026-10-09T20:28:54.252Z" },
+ { url = "https://files.pythonhosted.org/packages/23/2d/eb40652ea74f96df859c7c5d38f9997f420b9dc404300dda0c5745327a6f/multidict-7.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b4908e17867930b7ac77f89a18dc67308c67c511f037d8580489be86fb585912", size = 476169, upload-time = "2026-10-09T20:28:56.819Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d0/7454ab8335bc4ec9f8abcc4c83a314bab060f22db6da8436b396e07ed21a/multidict-7.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9b24e1f93b9b586ec03bc7bea1bf021ec90bf2528c729195028a3ca1c266b3f9", size = 420459, upload-time = "2026-10-09T20:28:58.744Z" },
+ { url = "https://files.pythonhosted.org/packages/60/7a/cedb46b287b720a2c4eda2448faf162612d0b718beb32581e35ccd9219b3/multidict-7.1.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5c93473d0d7cd9bbb370973a9679a62f381c7050d7dff4ad6aaa92e8650f5a79", size = 483071, upload-time = "2026-10-09T20:29:01.114Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/11/e7ded22b008546dd30ae8c979aa5ac3282cad98876f7d5bbe93c1f2409a0/multidict-7.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf14cfcc30b097583d698a6e2b8b68c9bcffab277c485d481881958360c2938d", size = 467470, upload-time = "2026-10-09T20:29:03.388Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/f3/0136144cb0b1731fd93475cea75d974a55009358d3bf61dd55e2709a3dca/multidict-7.1.0-cp314-cp314-win32.whl", hash = "sha256:86bc779a0896e59e4be30a5be5cd6eeffd0b40b6f0e75e730218736b7bfc6f5c", size = 80205, upload-time = "2026-10-09T20:29:05.736Z" },
+ { url = "https://files.pythonhosted.org/packages/74/d9/a62a690d780febc171026d3e3c5700fc8387aa3e5f77cd04ada33d23d3f4/multidict-7.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:6c9fd50f636a8fa9cb6324cd3eac962fec2bc5bb432452a3b583583a1059acfc", size = 92923, upload-time = "2026-10-09T20:29:07.337Z" },
+ { url = "https://files.pythonhosted.org/packages/84/08/eb7a1c34dc37c5f2c6aef52e6861e6f1cdfc6c685dcb72581eb218c00953/multidict-7.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:e6906aa4bc62cde2c8aeb8a99a7b4401b241e274ae7b11df67d863d61ab3d5de", size = 92851, upload-time = "2026-10-09T20:29:09.15Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/3a/019abd746e8fbed9edd74b81259f20b278c011cef918868506d9b5bd6566/multidict-7.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fabfdd4cf97db033196b51af46b8a681d4785c2a66347f2a5af1b4bbb1182629", size = 177429, upload-time = "2026-10-09T20:29:10.989Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/41/c8dda935231305d9b83c4a4cc5b5b9e323615328d4704f00ba2cf6d89916/multidict-7.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a177a0ee5cf19931dcaeb3f662bc562754cfa4f4ace2351d9da24a954ef7db94", size = 102003, upload-time = "2026-10-09T20:29:12.78Z" },
+ { url = "https://files.pythonhosted.org/packages/af/23/3c3e79a222eaaa59a32648cec34709d848657be5339f96876075731e7f8e/multidict-7.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0ae91de396d5c4ac97cb24dbada3d5c91a51454781e0a70476b008f4e879e4f0", size = 104861, upload-time = "2026-10-09T20:29:14.591Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/5b/04637e7cea5729466fb89048520fde167c5404df1beedcc86f18aeabed7c/multidict-7.1.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:160bdb3520fdadcaa21e1b98aab2e011265070814ecab3804eb61674becbd400", size = 463221, upload-time = "2026-10-09T20:29:16.697Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/42/b121767de213a9774399c200cb877b19c2e1e5a0b0a6bc8407c7325fa37e/multidict-7.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c2144785e42527404bbd5cfd11981fee4abe59a22aded0e498eb711a831d3f3", size = 502241, upload-time = "2026-10-09T20:29:18.657Z" },
+ { url = "https://files.pythonhosted.org/packages/47/0a/1cccd17ebf39776df7d8a6c99d066fc3ac1823d044cfe86a22ebbf42b841/multidict-7.1.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7d0b4fec6a8d02d7e95de5cfa913261820f1ce04bd4c0381924de0da523179b8", size = 457128, upload-time = "2026-10-09T20:29:20.589Z" },
+ { url = "https://files.pythonhosted.org/packages/19/a9/0616d20dee16255f8737d9017288d9304dabf3695ac0071baca25d00c713/multidict-7.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:943a9bce22180ad0f4d32d1b402a0949a4ecfe5a1257b47f54a1b51981d81b86", size = 511842, upload-time = "2026-10-09T20:29:22.569Z" },
+ { url = "https://files.pythonhosted.org/packages/91/68/5a406b82ecfeee50c097c05fd519c836a61815d81c4a9a8c523d90fb4b35/multidict-7.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f76ceb623f7ff50df46ac57e1587c479d87a5766319c4f43d0c0a5158896afab", size = 519852, upload-time = "2026-10-09T20:29:24.527Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/da/d42234f9d4f0e33885c70149112bf62aa4436045090c15cec0810ada0b2f/multidict-7.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98beff85392ce435b28a0971ec21cade61ce8be8b632c9d855475a28ef92d31a", size = 500322, upload-time = "2026-10-09T20:29:26.569Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/c6/323e921a9812a6ea82978d4e1d9713e0b709023be037e6e4c14efe5472eb/multidict-7.1.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:71196ebb8d523148e5975396a444de02367f204b53b14e26794c96b2be0ed742", size = 444070, upload-time = "2026-10-09T20:29:28.52Z" },
+ { url = "https://files.pythonhosted.org/packages/93/87/109b7170de2168294f3e562f9d10caaaf946628d614b5a57246279acc1a6/multidict-7.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:35534b366410a36bb3d6f788691e37a76e4d1da48326b0ada3e5032580dd76af", size = 484862, upload-time = "2026-10-09T20:29:30.504Z" },
+ { url = "https://files.pythonhosted.org/packages/51/cf/7f1f63c9cf13f47036c0c64c607e6eee87c94076e3c6876abb6990ee62ad/multidict-7.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b4674b12701c3fcbdf7f88b9e4479701c93bec5da9eb576140d5fcc0092990af", size = 457314, upload-time = "2026-10-09T20:29:32.6Z" },
+ { url = "https://files.pythonhosted.org/packages/93/ce/7de8b4ee6a847cc951915c279fc388127ec32a7c14aa7a01c4ad692be575/multidict-7.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ead852a5e906a43fcb6784eeac480f6a67919a51d480c1f80d32ddf9d615475", size = 462672, upload-time = "2026-10-09T20:29:34.776Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/6c/02dd089475983b1f5b8729319caa0b02fa3a60b527f27c86b2d53c24ca11/multidict-7.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:afe36ca503c2ffe30fb6df82b20389fa3c4035b5d65888a61310921cf3ae91c5", size = 498109, upload-time = "2026-10-09T20:29:36.763Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/b9bdc59aa8e3eaf1f9e5d2460bbe0c428a01250d44417f8ccf965a8f6fa6/multidict-7.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a5f0bebb10aae010d3c9ee3abaf83ab2069c718457aea09c15532355dd7e061f", size = 442260, upload-time = "2026-10-09T20:29:38.872Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/83/21b044885ab81bf5c03ec5c4c16aa449977e428c5c122e38969481cec1df/multidict-7.1.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:b9d9b7d72975521434368fe8aed3f6b522060bf271adabaa5ca6c87c0c08e168", size = 498916, upload-time = "2026-10-09T20:29:41.091Z" },
+ { url = "https://files.pythonhosted.org/packages/95/8b/31686730092e349ee2255fa630945ca344fc62e76f0b18d65b0f3dbb0ecf/multidict-7.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:274023bf952f849e0d05eba28a4c1f65f9796430d2b09ec16539386c0f76554c", size = 493525, upload-time = "2026-10-09T20:29:43.192Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/d0/c9fddcd7ac42b70a46ac9cf15e21eb527874164e14d418fcb50ce7190a4b/multidict-7.1.0-cp314-cp314t-win32.whl", hash = "sha256:7e0bfa161df365ba3c88899ee3b7c94755200967284bdedef8c1b8b43e2c0f2b", size = 86481, upload-time = "2026-10-09T20:29:45.188Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ab/ce727c06680e72b5d381caa8587f561cb6fea1bfab6ba20c877019768a6f/multidict-7.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:34a35be8fb82d37087e8176aba907b9459f03d0e293c80f574c6337a436f4eaa", size = 99676, upload-time = "2026-10-09T20:29:47.441Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/e7/d116d7ce514d04d9bf63774ed55e8033e2ce15ab5655a597bb947c53dfb8/multidict-7.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:d7dd46a8fcd7653c09ebe67eae9d4cb6636c7a905d9cbaf587dabcbd4eca6013", size = 98726, upload-time = "2026-10-09T20:29:49.365Z" },
+ { url = "https://files.pythonhosted.org/packages/28/42/963e2e38ffd79487b24775841cfe2abb85aa1fa08ee152afc4a9b64e1cdd/multidict-7.1.0-cp315-cp315-android_24_x86_64.whl", hash = "sha256:852c921217f330b3e81a822647ebadeae7e42cf503ec1992d0bfbc90121c09fb", size = 100450, upload-time = "2026-10-09T20:29:51.486Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/6e/fbc4721aa0eaea2dff3274ff44e94f60a89841bbb4cdaecd9faa4eb64aa7/multidict-7.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cbec738d2ad551c6f70955d7eec95e339380ee1564e2afe86bfee05fed52ceec", size = 95440, upload-time = "2026-10-09T20:29:53.929Z" },
+ { url = "https://files.pythonhosted.org/packages/76/66/045aee749b599560d5348bb1bee5c9d61d8b12f31d16e0594778ef3472de/multidict-7.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a5f721a2437390ab69c10c6df5c142478d399af8dfb02e6d823cf2358e8a4748", size = 97496, upload-time = "2026-10-09T20:29:55.808Z" },
+ { url = "https://files.pythonhosted.org/packages/97/79/f2916b81324d629eba363f760fba26cafef5cb437489c24969ada979508d/multidict-7.1.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:99cf27791129d37e191ff013bfc29bf6631c29edb21680c00978567b91fc5d6b", size = 163241, upload-time = "2026-10-09T20:29:57.813Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/f6/2aab2b2d7bf2d69204bde0cdabfefe4df3d1954f3f156bfc53714917e757/multidict-7.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2ba6611fc93c4b169d0e0ea376ebf4b8a529933d1f5f2c2ec7d8f8b93ef58ec2", size = 94586, upload-time = "2026-10-09T20:30:00.201Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/83/cf690ab80a0db0f50b300a7141f45dbf1bbbb6b32457f74bb55f47d38f5c/multidict-7.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c45629c0049fbdef932dbe408ac2b271fdc8c7d9962ca31160f4a0fc3455fe4f", size = 97573, upload-time = "2026-10-09T20:30:02.675Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/2e/951c1412c9803e8f87c8673305be1e448761c940cd867ccfdd3e6a551dfd/multidict-7.1.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cf606cfe3f67984b4064ac605d71e1eba12515fbabf5bd5a34a8952b8800dc66", size = 453476, upload-time = "2026-10-09T20:30:04.622Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/55/12f9f95fc65470bf481ad3d1c0e0fc20094720d0af1985119656f2b4be25/multidict-7.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50fdfcb03be719d9573597b095b1175d2e9d0b30d065791dfd9fca727c499442", size = 473078, upload-time = "2026-10-09T20:30:06.986Z" },
+ { url = "https://files.pythonhosted.org/packages/87/31/ebe216194a68934de7053d582cde62c839bef4dbc4ae67e9bbccf80a61d2/multidict-7.1.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a8bba9d1f6db4ef2a6ebfc937a65d36e80e3aada00b382eaf56fea8f639322d5", size = 435633, upload-time = "2026-10-09T20:30:09.021Z" },
+ { url = "https://files.pythonhosted.org/packages/46/e5/4aea764cd6a1d326d91f2bb92b1c8c39c3f5a8da624e7b20b88179dffb72/multidict-7.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46d4af0afc6eb9867b3ae50605787c80b868e2f52eac3801246034925fe578b8", size = 488656, upload-time = "2026-10-09T20:30:11.119Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f8/b1e5935233fa4b504b1a58b584fcdf03526635e4b5f268f716be9108a1a4/multidict-7.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5cc58ebb731200ddb64d55f1b345630fb5f7a8138cdbd242af9dce964a7cb03d", size = 502311, upload-time = "2026-10-09T20:30:13.396Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/8e/cde07147b6fc56b9ae0fe01d9c0bebd730b2be2c3052d3a5a117e13f561e/multidict-7.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b87ad54e8d4adeb0a1f04889504d6ec7f04fb02609220810f51f1b6c66bc1cc", size = 477470, upload-time = "2026-10-09T20:30:15.894Z" },
+ { url = "https://files.pythonhosted.org/packages/68/a1/11ac1880eacddcf698aa227ba9387cda60128e38034eda182cf4585109ee/multidict-7.1.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:44f7e5dd83a615636b80182bdf446ece57ed61d5d51854acc5d9840631136d4e", size = 430088, upload-time = "2026-10-09T20:30:18.972Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/5a/09926de0ec910704507a21c4f1ad76026aab9a5657ced868c57bbbe9324b/multidict-7.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:9bc5e7f843d14a167cdc26fe2d22f6f3aa2feb57919cf3ff034262a57d8d95d0", size = 460632, upload-time = "2026-10-09T20:30:21.45Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/36/cc51eb3ffb09f475326bbf868c6af2805fbbe9175122ce6d7a65aa06d6c1/multidict-7.1.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:0631eb5f49f67de10bbdc3f64141326dbc62e8d319900966648381ce0845d8ca", size = 434056, upload-time = "2026-10-09T20:30:23.679Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f4/9e0e4dce595573eba1555495ac9ca6b5f7f6e14cf6c3a5cd87ce5bd33d6f/multidict-7.1.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:d1b1b32f3c32f734dde8f36ac1df8e275e768a7b333241cd637cb2538628a4b4", size = 453014, upload-time = "2026-10-09T20:30:26.192Z" },
+ { url = "https://files.pythonhosted.org/packages/96/14/993a7ac9f3592f5628a12f6bc2495f5cce5814b883170e06f487dede6969/multidict-7.1.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:159976f9c40f96e3fe0952b708846a43a76bacb114e9cc828816f5080bddd5ec", size = 476343, upload-time = "2026-10-09T20:30:28.532Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/da/c2147c1e225a676ee0c97f97bb935fb8a70821bc12df240491bf9f140801/multidict-7.1.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:81a0e08c64dfdad27dab687b96f572b23bafa1999a39d1b6f70b3ddbb73e8bd0", size = 426813, upload-time = "2026-10-09T20:30:31.015Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/fa/8858b260a6662035670759e7b3a7da9decc227296491749073e76202de93/multidict-7.1.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:4e11e7299079718c78f8147e7206c22fe35bab4466d38992420795288a0b8096", size = 483882, upload-time = "2026-10-09T20:30:33.509Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/43/d42dc515d56d506f437e8a19c4f604719a306f8d2b60f70a14be34ac9231/multidict-7.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:34d2ee98e15d5cfe782a431bc913fce3b58cf3fdb34fcb437aeb275cdf9007ab", size = 469953, upload-time = "2026-10-09T20:30:35.996Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/bd/1b6fb52f7e082b4d49b069d362169a2e6dbc43800f152f33c8f0e8933117/multidict-7.1.0-cp315-cp315-win32.whl", hash = "sha256:f376224572d1f5da1c871f969ab04765727f180e70d012d93e07bfc08442c64b", size = 80202, upload-time = "2026-10-09T20:30:38.121Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/26/5dec513dec950825529037703da89a597dc0b157e7b1b8d0304dd9e672cc/multidict-7.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:67fcf28db77b385820881521db7435e9f1c607cfaf07db6eb78aa9d1146bde86", size = 92921, upload-time = "2026-10-09T20:30:40.507Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/eb/fd62025f8cd3dad6f936df8bd7ff2642e2fe3fe0166ef2fc75699ee57cac/multidict-7.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:c7aafa4dd2f702ee2198005d6cba4309c1e25ed1c201d77beddefa47411bead8", size = 92848, upload-time = "2026-10-09T20:30:43.026Z" },
+ { url = "https://files.pythonhosted.org/packages/42/f4/2dcd45731d2f57b87211a1dab1c29a72c6a4a527f370851c3e4cef5c4445/multidict-7.1.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:1348ddc076251cd542f4a99ccda4b7c1f8444e8ab489d3541a978ca5901c7c1f", size = 177634, upload-time = "2026-10-09T20:30:45.125Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/d6/d57b12a5bb19396a99ae66ec48ff69f764cc2c83e83bd9a003bd5ed5802a/multidict-7.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:9267bf8261a779abb2a6eab5f107f5db85b2d1745f2494081c731aaf28738ce3", size = 101954, upload-time = "2026-10-09T20:30:47.416Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8b/374e3f6e4581b8f8425712214cad854ff9370019483aaa49e6207c160adf/multidict-7.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:8a844b8b1685f38a2e8b2f3213b286e2a7abfe67508381780a0d4599ac337c1c", size = 105056, upload-time = "2026-10-09T20:30:49.618Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/0a/6c89e2179a84eaa78eec658b9a05f10d3e22654708d7b2e3ebdf0faa0614/multidict-7.1.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:18a447d46a3a2f1e61b365cbf5627db7030fdb707dad70c4f2760e5144166ecc", size = 467374, upload-time = "2026-10-09T20:30:51.933Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/62/431ed831b5e9b94c75ed0a410786d07176e879603601570eb25594039200/multidict-7.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88811f890db240a1c82bf0bcd52973763707a552c8113ac3fcebca183afb2fa8", size = 504024, upload-time = "2026-10-09T20:30:54.701Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/dc/52e4204538a7824e407e531b742ff6f7a2ff614dd40382d29a62755699cb/multidict-7.1.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a60b720c329c0007feae692b7bf91cf17b3f9bd3727be96cc6f9a3336651041b", size = 460139, upload-time = "2026-10-09T20:30:57.193Z" },
+ { url = "https://files.pythonhosted.org/packages/22/4c/0228d61a8fee69a7ac4d8d0426aa1810b9c36b679a8c51844de701230073/multidict-7.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b117ed1cd1a23df0902461c38093408b95971833dcee629112acda25b603c8d0", size = 513162, upload-time = "2026-10-09T20:30:59.662Z" },
+ { url = "https://files.pythonhosted.org/packages/56/50/a0b28bf4f036897bbd44c8761fbb384c014c34f621687799d07345b62820/multidict-7.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bbcae7a54050b7ad7bc7bf425ba63dea7d2cd31a92246ba787a2ce69a9b98dbc", size = 520312, upload-time = "2026-10-09T20:31:02.515Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/1d/ba96f77c24174eff9f6521cc7c83958f27598b924a9f7b6aca5d11c73f35/multidict-7.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4cba2b0b9235fe10e12301d6b4cfba0f353fa668d635f6e988b03623c2cd42ba", size = 502953, upload-time = "2026-10-09T20:31:05.187Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f2/14c3ffb649cf45a629ec38ca757a493f0caf729e3f3580816dc7779e4caa/multidict-7.1.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:535173fbcc3933d84f9929d49d7a59a0faec259ee07d07c34c7d2a980b4e3683", size = 451497, upload-time = "2026-10-09T20:31:07.597Z" },
+ { url = "https://files.pythonhosted.org/packages/55/df/fa4f8f6ee2314bdd3e4bc830c25f1d8a737188198abeaf105c73454f0b95/multidict-7.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ea027bdeca1d7e498237634ee4e3a852e2723eef39996dec0ff0f77dff8a2336", size = 486212, upload-time = "2026-10-09T20:31:10.178Z" },
+ { url = "https://files.pythonhosted.org/packages/32/5e/3227762b04a8ff1a90359ee2c04af9ed095fe3892e101705fa4233b51514/multidict-7.1.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:507151e1e3dee95e9e8159e329aed4f75aa5205ecd6505a4f6be546890eafbe1", size = 459864, upload-time = "2026-10-09T20:31:13.044Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/04/58524c7e82176438a48704a687fb383cc943dd7075c67b5bea288043b504/multidict-7.1.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9c10791e9f5ef132effc8fdce2009482c1cfb26618c5fc1b7952a47dd5eb632e", size = 465861, upload-time = "2026-10-09T20:31:15.503Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d6/28e549b6ec0c829647de4c75cf32e4c7ea0d0e87b91dfb399292f630c365/multidict-7.1.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f16ac8af2804855d3cae5fc3c5ab609c9fd0fc8ecacd92579c05ed3c173396fd", size = 498865, upload-time = "2026-10-09T20:31:18.548Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/ac/3006640586b4d33442c53989b78fc290c8eae5fab5b62a31ac7a4c220e69/multidict-7.1.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:b78de22bae456a976f33df34d598dfd16edc9a03df8f4cc8b7c17bdba4c97b4a", size = 449885, upload-time = "2026-10-09T20:31:21.03Z" },
+ { url = "https://files.pythonhosted.org/packages/80/7e/1ece407cee9f9aff9ace9e44379ed937bccb790936ae448feb79a82c362e/multidict-7.1.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:db77888081431aaa69f3fd3480891746ddce6c2a571f6201869a24e2f06cf423", size = 500237, upload-time = "2026-10-09T20:31:23.611Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/4c/c052b700ffcf689454848ee81ba87bedfdc10caff592c0d2a327c43939a5/multidict-7.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c39dfcaa0bf23443474c0cb58d8d8aea9529c1841d99654cb38e4dada7b1948a", size = 495143, upload-time = "2026-10-09T20:31:26.237Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/ef/9ddad94c32fb0bdfc5ad0942d06ae6699bd30d261b0ff59b94810a133238/multidict-7.1.0-cp315-cp315t-win32.whl", hash = "sha256:16b21164797bde6f417066d02775975cc2e15ab8abf80efa55fe85e0b4894020", size = 86526, upload-time = "2026-10-09T20:31:28.734Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/ce/2efa1f32a07adfe041d48c6d04c08b6f9db66b8512c765983172399b2855/multidict-7.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:55392202cb374dd1a1f89a8ce1586644870d9e936752059d053e576acc50bc89", size = 99671, upload-time = "2026-10-09T20:31:31.282Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/b3/a44c301fe13716c5f2c08afd89a481018988cde4b337b36e28a2b0fcf3a3/multidict-7.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:f979a077d1c0a9a36dd4fab0d3a36b8de7b593bf935e13df85a380395b2c11ad", size = 98893, upload-time = "2026-10-09T20:31:33.62Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/86/a3de309c5e28ee85b314d0e3ba0e0dea6fd361c313322a05e67be4656e1e/multidict-7.1.0-py3-none-any.whl", hash = "sha256:d9ef29cfd98e17085b4f91bba8fa1570bec6787d5c52ce653ed33a58785585d0", size = 28087, upload-time = "2026-10-09T20:31:35.945Z" },
+]
+
+[[package]]
+name = "openai"
+version = "2.54.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "httpx" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/9a/8c75e8c8a5b407a0586faeb2afac91674ff955c191ecc1d6d3b6669f6788/openai-2.54.0.tar.gz", hash = "sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa", size = 1100285, upload-time = "2026-08-11T18:46:59.035Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl", hash = "sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b", size = 1660351, upload-time = "2026-08-11T18:46:56.684Z" },
+]
+
+[[package]]
+name = "opensandbox"
+version = "0.1.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "httpx" },
+ { name = "pydantic" },
+ { name = "python-dateutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/2a/ab3cc141e041f71a373c97fcda8749dba9328f1b9bf80401378c0611556f/opensandbox-0.1.9.tar.gz", hash = "sha256:670fbf292c498f8467963d21e91ade9ea8b8f63f4ef18d18fff9581e0952ec03", size = 160034, upload-time = "2026-05-12T12:27:20.692Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4e/9b/553f8d7a30eddb12785711b2a1c682386878e2bb95450acd806f9fa62930/opensandbox-0.1.9-py3-none-any.whl", hash = "sha256:17faed35b60a982fee5a643fed8e4e12f041e5432d5ea0665d2828d1f2082759", size = 360945, upload-time = "2026-05-12T12:27:19.465Z" },
+]
+
+[[package]]
+name = "opentelemetry-api"
+version = "1.45.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2e/02/6e0ae9cc61bd3169d401077b507b3ebc344745171e1051ab430be012dcd9/opentelemetry_api-1.45.1.tar.gz", hash = "sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75", size = 72804, upload-time = "2026-10-06T17:32:58.133Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/41/f7dcf80b81ee8e71c1a2b59f14208bc723edbd89ed027a73b175abf6348e/opentelemetry_api-1.45.1-py3-none-any.whl", hash = "sha256:b31553efa588ae44bc306f863c785c5333a9ecc091248c6ee68b4b6c87fdedfb", size = 60256, upload-time = "2026-10-06T17:32:33.506Z" },
]
[[package]]
name = "packaging"
version = "26.3"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/packaging/26.3/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79" }
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
+]
+
+[[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.13.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/e6/d0abbb256b44e6a05748c6942511ff67db8dbb068de0c35b24bf1b395b09/platformdirs-4.13.5.tar.gz", hash = "sha256:05831adf87d392760f4b5c3a0a95579ab5a10b18b9b9bd43680dcb47544788a0", size = 62362, upload-time = "2026-10-19T01:02:11.778Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/packaging/26.3/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c" },
+ { url = "https://files.pythonhosted.org/packages/6c/8a/1d8cb3842909dc193a73835adedf412e32e18f8ffd59f43983949622fe6f/platformdirs-4.13.5-py3-none-any.whl", hash = "sha256:53d3ec4280b815c1e0b02edad7f9ee8fda1ba9c0865dd9c2e6e63849ce82fc1e", size = 32779, upload-time = "2026-10-19T01:02:10.17Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pluggy/1.6.0/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" }
+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 = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pluggy/1.6.0/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" },
+ { 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 = "postgrest"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "deprecation" },
+ { name = "httpx", extra = ["http2"] },
+ { name = "pydantic" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8c/63/bbd2229c0081685aad8eb713b5bbca91c83dd6afebd545fb63282cb6ed9f/postgrest-2.33.0.tar.gz", hash = "sha256:8813be05d129e8fedc1a9649487ffe524242c33ed380e187edcd16c414915217", size = 15165, upload-time = "2026-10-15T19:14:17.763Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bb/65/41a44b64da5b4d9c8bb1e6bbc6a0a71ff9315236438cc5c63bf504ececf9/postgrest-2.33.0-py3-none-any.whl", hash = "sha256:802bb30028b897f929e8b0b64db20d949b0c1c58e5804bcc7da02faebf2c03ae", size = 23870, upload-time = "2026-10-15T19:14:16.659Z" },
+]
+
+[[package]]
+name = "propcache"
+version = "0.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/9a/9fbf4e4ec0c2d7f1c32519fff782ef467859b8faa9fbc5331a96f6395d43/propcache-0.5.4.tar.gz", hash = "sha256:ff6b113f50bc066a698db5d944d2c6dc7507168dd3341e255a8892fd0715a558", size = 61545, upload-time = "2026-09-16T00:17:14.386Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/dd/997704118aea215cc5f65c277f5323657b4ba44c1f9a32fb11c8064e4997/propcache-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b77c313314524ca9c38fbd70f73515d04597ac58c40c939bc0e71eeb4abff680", size = 87225, upload-time = "2026-09-16T00:13:43.864Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/6d/ceeca1762ed51230c08d557591404f844ecef5e294550136155572f7faae/propcache-0.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f911c395cef73c510bac566da9507bb6a43e7763d0c79138dc60ee53f11207e", size = 50809, upload-time = "2026-09-16T00:13:45.33Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/9a/6be90814d8762952594a9e380161802541bff690f3a2e011dcc28ec193ce/propcache-0.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d83b12902eb8bce151259c86c03ba746600b2d994543de46e370cecf96c452f2", size = 52552, upload-time = "2026-09-16T00:13:46.48Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d1/7fd3ffb5ca0e669a8fbf55d9fbb17c50ff5f44becfefcab27a9b9b67908f/propcache-0.5.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9281e922c072158c91974d4589f1dbe0fee6d467f284c28e463f9f5a4d933f4", size = 226804, upload-time = "2026-09-16T00:13:47.739Z" },
+ { url = "https://files.pythonhosted.org/packages/74/87/a3e199c45b26587f073db655d19c30d2144b0f883827ba6ab77acf5c7d45/propcache-0.5.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9f3551b8a35c1df3e7ea4d2d86edee15f0dde1bddd434a71744048683544d0ef", size = 234452, upload-time = "2026-09-16T00:13:48.98Z" },
+ { url = "https://files.pythonhosted.org/packages/12/7c/08c15c7df256f94a6b4563f74165c3242fb554cdd1909b661d093c31b976/propcache-0.5.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ec6a85f424afa8d23e0d9a094e5dbb6eda01da91c92b9183cd433768247ffc97", size = 240399, upload-time = "2026-09-16T00:13:50.416Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f574e460d1c8a08384a016fdb09ccf3543433263ed6b2f97104f979e64ea57c2", size = 224251, upload-time = "2026-09-16T00:13:51.874Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/53/54bd510bb5d473edf66914602885226187218b4e3a5017e9dd80bceea41f/propcache-0.5.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8e017eeb7482bed34cdb0d61cf2bcfc88d104bbab296a17cd16a6af8aabc70e", size = 202721, upload-time = "2026-09-16T00:13:53.524Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/b4/a468700d0526dfb2ca6af5d790125de5894acc7d8ecd99243ecedfa4c4cc/propcache-0.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f273dcf7149a50527c4fd1f55cfe9eac0f60753f5af544b4c9352578e20c0874", size = 219148, upload-time = "2026-09-16T00:13:55.068Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/d4/bf563e19ac9a5cc47113431337fdf2ee3573859f3f3e0a58e59833e9b75d/propcache-0.5.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:fc2461ecc45f17893f8207e73b46ea8ba93e33630e51cf4af3fbc21d47462b1a", size = 211011, upload-time = "2026-09-16T00:13:56.588Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/3d/0189b6537f4a8cd795e685a130a6f47aeda78a5b7b062574705cffc685ca/propcache-0.5.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:279655a16973f1ee2bd2fe79973137681642fd9ae0d89215bba263726eb0dc3a", size = 228148, upload-time = "2026-09-16T00:13:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/b7/59c16e245a549df202b4ebe2de91fa58a67dc4373df9840e372a64224dee/propcache-0.5.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e9f165403b81fea7e89c932d89046a1e3d9a3a60e8d7ef2f249dccdcb0982bf5", size = 201432, upload-time = "2026-09-16T00:13:59.453Z" },
+ { url = "https://files.pythonhosted.org/packages/41/0b/7b19eb20bb0b1f9476d08f6e387ae094dc6870fc06d459ea1f35f28b25d3/propcache-0.5.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1783582065a1f07f9d9ee1e992e13f15d7dc8fb1eb3a7476d43eb3f2e69d26bb", size = 228923, upload-time = "2026-09-16T00:14:00.731Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/f9/b1a0bd47218216b935d019912d6fec1676ae7c07f15fa82bfb54b8d58976/propcache-0.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d605bb239b796e82a81c6709548b2bd460ab73b4590cb0c83de8a2dd9694d0f", size = 218327, upload-time = "2026-09-16T00:14:02.091Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c0/51e5d1ad504e9529831606534c48e272db751072eee9ff07e4abe37e50bd/propcache-0.5.4-cp310-cp310-win32.whl", hash = "sha256:141fdbd73748db0cf7636035030aaac383d2efde8f34e7bc24594cc776d225b8", size = 43096, upload-time = "2026-09-16T00:14:03.427Z" },
+ { url = "https://files.pythonhosted.org/packages/80/57/0a0b4b1122f4ed5bcd1c626d910003cdf5282c2a12f8a1cfa17970b3ded2/propcache-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:146f48a9e4812611a7581003b1a39de56c34967046310c4171a68ef908c9a745", size = 46589, upload-time = "2026-09-16T00:14:04.581Z" },
+ { url = "https://files.pythonhosted.org/packages/66/87/e71b24adc8ece61782ba3d6f3879e6a07fdb85bce21a9c529524184e3ec6/propcache-0.5.4-cp310-cp310-win_arm64.whl", hash = "sha256:6c7599df2b57ebeea8de011b5f2f7b85de95e76037d43d34b95e328430275487", size = 43982, upload-time = "2026-09-16T00:14:05.728Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/40/14b21e505b7921617466576423f188a5c9caddfdaa1cf4b2b8a83d8fe216/propcache-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:897d1ddf6716e8f47200f7aad9a0efa6cc7586df66c6defa572f9eab379c078e", size = 86393, upload-time = "2026-09-16T00:14:06.9Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/4b/5a52e1a7b43563f7d408814194bb23cc8bf214eb6b86639b667a33a8d0d0/propcache-0.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9cbfff4423eef4cc6cafc021469641a2b835f610b2647a6c5281903e21b8670d", size = 50431, upload-time = "2026-09-16T00:14:08.025Z" },
+ { url = "https://files.pythonhosted.org/packages/05/cf/b5248180bf056cc76acc60c9c6e8c0ebbfdbd1c6cffd31fd14996927b7c8/propcache-0.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fc24f209c1b7f7f688b66b98293954f5504279760999b58920ee12dd8471c1d", size = 52116, upload-time = "2026-09-16T00:14:09.114Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a8/7c6cd6bfead1a11f2e411e688640e6d26574cb0bde7dcaa7423b0b65ed7a/propcache-0.5.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62530ca89187827e4a4fe733f971abe81a7542eeea48ff61995f19b64d7199c8", size = 238729, upload-time = "2026-09-16T00:14:10.357Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/b4/442715b2e980df51be52d203549279e027728f24c80b00b5e525e31cd5ea/propcache-0.5.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fc3f7599528db40b1efa0889a620116e2704144495273d66066e8164e45838", size = 246121, upload-time = "2026-09-16T00:14:11.735Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/5d/df0684fc2b1732a01a7bec26d7897369022712422d25b09c37ce7dbc88a2/propcache-0.5.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f2d880ff60f45898f4acfa152aac8d04e3ee627d90ff4003491bf92239d5757", size = 251735, upload-time = "2026-09-16T00:14:13.053Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/06/519a5ebb48b6f94beb48396e55c905f12246a25c3a3608a7ec7bceabf50e/propcache-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e9368e87a3efc285e559131092c5db643eb8e56de4ee42064d5baec22ef2bb5", size = 235381, upload-time = "2026-09-16T00:14:14.398Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/07/1e0a9bb310830f2245edbd5cd3c6d24a783c053c4efd8e08e386e513c940/propcache-0.5.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:004e685b315646c410771836e72a44f143bbe624f29653a42687815069a303d5", size = 208973, upload-time = "2026-09-16T00:14:15.715Z" },
+ { url = "https://files.pythonhosted.org/packages/62/5c/9324fab27d6088eecc47fe4332bf7aaf8c1ded93c36f558391e8a06d41a7/propcache-0.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:594eb4c6ec35e7179b058481f4e9f02521b56de16fa577c4b85c76fb1bf8a9f8", size = 233897, upload-time = "2026-09-16T00:14:17.25Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/a3/570d92fc952eae93b676f3a1568f4b89264102abd3c982ab6a9ebec58dcf/propcache-0.5.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2dba2f02d2d5c09ef8a0e6c1a42aeaa451f4be9898cb00b04fe98717da2eb23b", size = 223512, upload-time = "2026-09-16T00:14:18.87Z" },
+ { url = "https://files.pythonhosted.org/packages/65/47/26810d889d89bba31db397e6a88f8984af775f5ed6bad0a29dce84324cff/propcache-0.5.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c3ef2818d63bc86071e9d2989ae75a1bc32b8f7059cfd9f5abbbee70c32e2ed6", size = 239043, upload-time = "2026-09-16T00:14:20.366Z" },
+ { url = "https://files.pythonhosted.org/packages/89/2d/f9c47691aa024c8299a3afacd78d22a01ab57eb627b481b6089708e71017/propcache-0.5.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:dd2ac8f5b643454c2cc6b6118b13da16e88f4a6434fc3ba61aca384029f04f36", size = 208218, upload-time = "2026-09-16T00:14:21.801Z" },
+ { url = "https://files.pythonhosted.org/packages/74/6b/d510c0c378cabbf9d0ac7b663af6d00f2e9074073d93b20c85f24aa5c071/propcache-0.5.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4054acf80d40456a0537f2913b349718649d8d6458a14ab7f48d0ce28c30869d", size = 240301, upload-time = "2026-09-16T00:14:23.121Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/80/c80f6adaaa1e51f0db2dce8c9b3714d94ec45e358a21f9a1910b10b40a80/propcache-0.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e94adb1e7d39ff28a8bd8d8b8fbd1df6b9f40976dbe379134f1ce058e532dd", size = 230785, upload-time = "2026-09-16T00:14:24.458Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/e2/c32a7df3f39caa7f11b2eb37ea5b6960a6946f2bc7c4b8ff97bbdf6d6b6e/propcache-0.5.4-cp311-cp311-win32.whl", hash = "sha256:9f86f7259efe2c951f43e57d471c9b41daa5bfc7db9f67189059cf1ae6d77fd9", size = 42747, upload-time = "2026-09-16T00:14:25.715Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/8a/3db6a3543d8101263b4c52978b6276a04ead2caff2c5ab880d934f47bd89/propcache-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:e904d4d01f36bd6e197590be1533c44e06058771e0746dd073a8ebb3ef880858", size = 46268, upload-time = "2026-09-16T00:14:26.996Z" },
+ { url = "https://files.pythonhosted.org/packages/41/07/5222e2665bbf6e45847492ecbf3b9f3e4975a0ae300e5fd465df7d48ce55/propcache-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:d42a9a856a4a6e2f6c10f1318c07e7daa498d6593abe745c71dae4521a26ca39", size = 43547, upload-time = "2026-09-16T00:14:28.143Z" },
+ { url = "https://files.pythonhosted.org/packages/71/cd/348d58f142aebc4873345c6b31087629182ca6e0f2b3caeaa528cf882eba/propcache-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b28f41fa3b8c6900457f858ec5b03998f3a6d535fbc1bb2edec5961ea05ec429", size = 87285, upload-time = "2026-09-16T00:14:29.362Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f4/f3ffaee281b276da854ac1d7a6a506d26cbc62ea2e623756f1d0a4a1ba1a/propcache-0.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dcbf346a318a5e30063f547630b02bb787ce2f45b6368d5da143660b6a3835d8", size = 50984, upload-time = "2026-09-16T00:14:30.473Z" },
+ { url = "https://files.pythonhosted.org/packages/25/88/1d7df7201750b37765ef2b23bc1c526c028dadde80afa0f57a118fc01182/propcache-0.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87a3caecf8095e48dc72f84bfa42e23a848cf410cc9cc13031fba4869b706a21", size = 52460, upload-time = "2026-09-16T00:14:31.692Z" },
+ { url = "https://files.pythonhosted.org/packages/83/4f/48865bd02a16ee5236bc46166b2946f37b93e07b0eae355dac0be0b216ca/propcache-0.5.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60a64cbccaa11b7760ce705a14ada17ba459e7ca9f23ba587eb013821032d7ef", size = 251768, upload-time = "2026-09-16T00:14:32.908Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/19/3742a5eed62317b03b4002ee865dc9fd720308bdd0da1f29a5786c630311/propcache-0.5.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a74bfa37147cc08fb29df10bd9c16f40fa7f860cd3a6d2fff853323a94f6e17f", size = 257723, upload-time = "2026-09-16T00:14:34.267Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/d5/ee6350fb0be9122bb6c67082a876d34b90d980d100c106af4b81023e04f4/propcache-0.5.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4d7a54719b67338a305dca2ce6aafe366817df94ddfd4b5514374356f5ca546", size = 265597, upload-time = "2026-09-16T00:14:35.56Z" },
+ { url = "https://files.pythonhosted.org/packages/85/9f/83a07b6ec0e043c050cfdd35fb0cf1b7897b91d554d6eea293740309afe7/propcache-0.5.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2814ecd8e818f487bee4b0f921bc4d1c176cc5fc71ac0f072d0fa67eda4ac14b", size = 250424, upload-time = "2026-09-16T00:14:36.894Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2c/a763a8251f50fba042af0fb1f02bfec4b31381e40aff760db2be7b2e1f84/propcache-0.5.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6af4693716bfb03f1752ef1b30faa593db2c01d5272e9b8564a1549452a979ab", size = 216748, upload-time = "2026-09-16T00:14:38.369Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/e2/4d11bea8fd6a777149c6c20645f873952eab5de3a2497aa11648ec9ab6ab/propcache-0.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4fbc1a15dc8cd1689508758d626b372b1f09d28d9577667feaf9e6bfcd8efcbc", size = 246533, upload-time = "2026-09-16T00:14:39.82Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/36/6683597de4907e70c717e3588c541202c66086a72ff3db58be49de66e72c/propcache-0.5.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cdee8205a44d0be91bbac4c41b95d86641b72dfc7aef1279400e4fda3f26a937", size = 238173, upload-time = "2026-09-16T00:14:41.259Z" },
+ { url = "https://files.pythonhosted.org/packages/85/84/cb08d79f1762daafeb2b030c470cd0c725c97b8ad67412457c6f35c53e9d/propcache-0.5.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9a2a8a50a93dee0268a860a07fa3b4bd968f8ce4dbd794957da772f395368526", size = 251128, upload-time = "2026-09-16T00:14:42.652Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/0d/41b848036db6621370c1f2e5471a7da8149c730f8552a5257567721f4576/propcache-0.5.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7ffafcbfc7b549ab940047e505c831eabac5e67de53e1bc174adbc5285c55944", size = 214821, upload-time = "2026-09-16T00:14:44.112Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b7/adfae4bf9c63bccf12e2d9690a175c6579047a6eec3b5a6a5f51428c15e2/propcache-0.5.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d1f5a500bfcbb2c0ab85e98a0dcd70f5899d34efe365a0187700369a79603031", size = 254793, upload-time = "2026-09-16T00:14:45.429Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6f/eeca9647245d5f92e87d53e5f14335bb42fce1a7e6842c8045b364eded8b/propcache-0.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a235f73d6e020855dc29dff012d920c02ee0feab8d73a24185a7569f4be1161", size = 247134, upload-time = "2026-09-16T00:14:46.976Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/a9/424e38838793d37160b4379c702f61c74c598fc6cd17204adbe3c554f7a8/propcache-0.5.4-cp312-cp312-win32.whl", hash = "sha256:b3083bfe87f95c756e610bd8025f26cbd1cd4aaa03a422f2d65efb7a97cd53d8", size = 43073, upload-time = "2026-09-16T00:14:48.338Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7b/6e8ef26f6d510a7916064fec68d55fcbfbdf7eb01e377480d66a122152d8/propcache-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:98914de2c4d7f0f9f4a8c6ea4bf05841f4175796941e3ef7d47eb718f22311fb", size = 46190, upload-time = "2026-09-16T00:14:49.99Z" },
+ { url = "https://files.pythonhosted.org/packages/08/b9/72028c5b56ced97f456de6aefa79435ca64d7f77af78ea8cf3c76fc5195f/propcache-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:8876b39961e33d912afe3c1bee18ee564fdad0206f873cc15d522756b7f50737", size = 43075, upload-time = "2026-09-16T00:14:51.155Z" },
+ { url = "https://files.pythonhosted.org/packages/78/4c/3b1365d58a667689e067e13d055fcd92bdf8d9a2fca3d9201b47ed5b3631/propcache-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:36c0d9db44b523ef93d03341b1c42d69ff01d673c053d1b1c6c3a363bcaa39ba", size = 85290, upload-time = "2026-09-16T00:14:52.342Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/61/5f9c29c3aa67c30238c4eadf95149b1d983a48f69b86b0cff927a7d6df13/propcache-0.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1d52a05dc417279f7e5c7618c5dfbbc29923aaf9bc0a5c1802ddcebf54c61a0", size = 50027, upload-time = "2026-09-16T00:14:53.67Z" },
+ { url = "https://files.pythonhosted.org/packages/25/7d/c1ab1ef09e9d4d835be5d58c0a32a1e1de8397abaa4e502a9d4141328cad/propcache-0.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44149f46500a0a41b95b4d99c2e586a77319539730607b9892974a092788b111", size = 51425, upload-time = "2026-09-16T00:14:54.826Z" },
+ { url = "https://files.pythonhosted.org/packages/73/36/0093091ebb270fcd1bc1f6e095f93b2e0ed7f1011c28837dc2dbe5f96b99/propcache-0.5.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbab5f5ff6897c81f355d079010cdae85b02e5a0b518b5251523b8ad8ae9ac3c", size = 233595, upload-time = "2026-09-16T00:14:56.09Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/0de9d4c8e05ce0be71b436919a216bd7fc5cc6e2691c0602295efb22b9ed/propcache-0.5.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c3e98c55bde2bcf7db3c70d1aed7ae9aa8aebbf19a250c66645cde44cdb8b867", size = 240318, upload-time = "2026-09-16T00:14:57.674Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/2b35e91455209b85ee98f7859583e0814fab57d3af0f2381aaee34c37304/propcache-0.5.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db3ae52ccc150dbc84704e9d642743897f3e1c54742ff34cacb661e52e3818a9", size = 246649, upload-time = "2026-09-16T00:14:59.352Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/74/08e6c1faf26ee2732023a3828787ba535557122774f4a386b1f715cbd8e0/propcache-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f85915e00dcb1cd9f2f890ead064ed40a27df06f0db65be427b29482ae357572", size = 234316, upload-time = "2026-09-16T00:15:00.696Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9a/08385733c9321c9bb78039d3ff31045e4fca962d9665023c4eb70f998819/propcache-0.5.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c2ba30a89035b57b73e00475de948521602f543d79ce01db10b04b36c4c76fc8", size = 204666, upload-time = "2026-09-16T00:15:02.019Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/f4/e87bc7629af9a14a752b218764a78742d73c2c563ac58315da6841f0cbe4/propcache-0.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae58f361bd5dae942717c65d3413b478c70aea9c462599e7b9adad3731db3894", size = 225900, upload-time = "2026-09-16T00:15:03.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/6d/11014938d3fe9bea2ea2dcf930f26ed565bfb2f5be3c756362ea48c92636/propcache-0.5.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:96f7c5c15656040ddcbc51e56dc59b58aa25999d743c126abd425b9766ab43e9", size = 219988, upload-time = "2026-09-16T00:15:04.811Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/72/fbf17c589f92c0b3bbf6709a425661f8ef2ed0d46b38985a7d7b5a0f6b91/propcache-0.5.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7cc528e760a8af06f2b13e9b9f362cd90c7c718ea61228a96dbd31ba16ed7f47", size = 233611, upload-time = "2026-09-16T00:15:06.498Z" },
+ { url = "https://files.pythonhosted.org/packages/55/7e/dbd637572a279692e5518d117274a9331bf5faac59f191d30e82521a3ec7/propcache-0.5.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:425f8cc86ab5018b4b8d4a23bc8e74d964bd3d757c3702e301aa79be76c53f6c", size = 204333, upload-time = "2026-09-16T00:15:07.961Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/5a/f99c92068f1e0f5c886899ce0e4a619db376ca98c5279d93f95bd86906af/propcache-0.5.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5793c7698a53f56f4a1889a4737c7eeb1b7ad0842fa6b1abca22913ff79c8c1", size = 235177, upload-time = "2026-09-16T00:15:09.334Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/28/95456fabd2daf6be89049a13fbf03341756014d2959c83d12957d4c49694/propcache-0.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c02c0e570c5c7e077b0181a9f3cdb7d4c3617d1cda6b5c95bd5d34022923d82c", size = 228982, upload-time = "2026-09-16T00:15:10.729Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/bb/df90f62c9cf7c93ea235f6f9405143bba802914607317266dd81fc8d737e/propcache-0.5.4-cp313-cp313-win32.whl", hash = "sha256:3e413d7a4a9b4866b7a761d6060d434b64d23cd35122eda3b026a0bbe8196b25", size = 42611, upload-time = "2026-09-16T00:15:12.111Z" },
+ { url = "https://files.pythonhosted.org/packages/01/bc/e0a7b84af04ec02d73a48aa71f091e1e4a2107e3074b7ce12195b66901f4/propcache-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:0c889f6fa84957bc7e8b4eab71fd16a0455068d5045e3aa40c733071d2b2fd77", size = 45342, upload-time = "2026-09-16T00:15:13.519Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/70/50b031cafe72a5c1878b903ee87303f71313345566bf3d6ec202e5ddc9ec/propcache-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:69fc35c0779522da366c563e5faf203ffc1f8ff0021d5b1337fa4efa5be73177", size = 42408, upload-time = "2026-09-16T00:15:14.788Z" },
+ { url = "https://files.pythonhosted.org/packages/33/c9/07e227b930c8ae513b8ef1aae3793499be097bffcdf7aee4fb8b33db4cd1/propcache-0.5.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e6720ba44ad7e72174314d0e1fb0172494cff5c73a3a8a2159c3d2402ff15565", size = 85933, upload-time = "2026-09-16T00:15:16.073Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/e1/6710bb44510c4e4a8e0f004bbaf3cecfd048141309c77bae56d4e5a6ebc1/propcache-0.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4cfe0a92ae30151869e67a4b5f5e105e4e03ad30b3f38e5211b5bf77d0881993", size = 50179, upload-time = "2026-09-16T00:15:17.377Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/22/b533b493d7025456f44518b33e53e000021a20fe7c27b88cf3d341df7186/propcache-0.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d759d05634f1b038fb625a66662a8c85e5a8fec912da381b5149ddac107482b", size = 51942, upload-time = "2026-09-16T00:15:18.589Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/74/70ac8430e28f21e442c7bcb964eb46c4363f6881ade4aa0e978bfd8d503a/propcache-0.5.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:251c63dd46a0659bb875cb254dc4c1e79ee91a847c737cd62373295afc2235dc", size = 232647, upload-time = "2026-09-16T00:15:19.905Z" },
+ { url = "https://files.pythonhosted.org/packages/72/95/f222f13b6fe623310be0eb61a673bf26df439ce27e563ca8e422d0818777/propcache-0.5.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a8d5ff04eb1f85698a78d20c62a14676e7b960dcafde09a388d60ad377d355d", size = 241541, upload-time = "2026-09-16T00:15:21.3Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/3e/763e370340db16115c5e63ad46e21ef0770a7f06928b3d3b62d8f8edfca4/propcache-0.5.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b9100a93b372418d8688f3f2a3e5b45c64d70ca4d6176e121aca1e3bfc1e32f", size = 245332, upload-time = "2026-09-16T00:15:22.802Z" },
+ { url = "https://files.pythonhosted.org/packages/96/d3/e97cd6f5de2176bd90ed4076c7a9b5e09d0f0b9687d00a576507988bb62c/propcache-0.5.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc07876cfb079b6f6f36d21ce75784ad6c2c6b563eeac0ed26c2fa2669b85df9", size = 232757, upload-time = "2026-09-16T00:15:24.374Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/4c/6766e5f60bcda26d244333aa71d0a702c1c9b21b251d543c7af5953d1eee/propcache-0.5.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0951315a6b3142ee2167404d707743f0157c110091342b1aa0accac5cf0e4acf", size = 204389, upload-time = "2026-09-16T00:15:25.667Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/5e/ec4bb09a70b26ea99d76a8292c3383b960b296de2b347ac9986678f1761c/propcache-0.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bee7d3aed13d56f54e681df38c3a23031bc9e3863f687d9d598825c9146acd7d", size = 228217, upload-time = "2026-09-16T00:15:27.11Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/7d/b53922ba7d9e5bf797324e63aa05906ec240871899f779628df068743e2d/propcache-0.5.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4e985382be6d15da8d0c2710a6fa7b9070fc9ecdeefb7f580e88373984ec8be3", size = 216947, upload-time = "2026-09-16T00:15:28.532Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/39/b62eee45e5ea4de094a258cbb3b01c1e856ca51ddfd95b43135c5effd1eb/propcache-0.5.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e9ab13760aa8b6d0881ae7cb04fd891d8d490cd2554ea8e79bb278399169bcc", size = 233457, upload-time = "2026-09-16T00:15:29.977Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/a9/feec61ed296d993db9dd097e0f6723e3f576a647722367547495e4c5b05c/propcache-0.5.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1b2f3bec4261a94019575481c726c29850f72e27907773c75b1de421e20e9f9d", size = 204131, upload-time = "2026-09-16T00:15:31.74Z" },
+ { url = "https://files.pythonhosted.org/packages/92/4d/411ef380cddad28dc001f1c6d75ec72c76cd3817030f68ec1ccfba0ec6c1/propcache-0.5.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:720cf832eb2d0b0dfee129cb3335a26f6ce3cc45ee1187e8f0731758caa16792", size = 234820, upload-time = "2026-09-16T00:15:33.087Z" },
+ { url = "https://files.pythonhosted.org/packages/15/37/c988229753629ef1cfd5198337a83e624780ea2b3787efe9e747c05aad2d/propcache-0.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fb0a5be8d9aa213150e8d8148a42aca4984b285bcad1e69587dc4298edd929b", size = 228350, upload-time = "2026-09-16T00:15:34.533Z" },
+ { url = "https://files.pythonhosted.org/packages/12/49/5ef1c5cf98591da3c5b952b39e6a298084cc1ce353bc70f85e82397a5036/propcache-0.5.4-cp314-cp314-win32.whl", hash = "sha256:30cc1cebaf9aef49db06357a50398323ae04d70460c0491837d026ab7d6452ea", size = 43578, upload-time = "2026-09-16T00:15:35.957Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/9e/a0ac821a2229186af5e2e3c3635a78abb23cfddca57f38513ab5d70420f3/propcache-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a095db8e15a6020db149ecbed6461939fe74f6acaa3ae8b702a1fe8c38cd983", size = 46304, upload-time = "2026-09-16T00:15:37.655Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/19/c8d0d36a9d16cba5dcee67d389c9333b988c8986a653a61c00a451817a46/propcache-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:45488d1a5f9ab5bd90aaa1ca20f50fe1922b8ffad71a2009d2adf41355897aac", size = 43440, upload-time = "2026-09-16T00:15:39.091Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/e9/42f1da77cacfc184e6ec929557ef653b7961bbf6f1da460b9221273948b3/propcache-0.5.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:53eaa697c4d0422ff4cb714d00231b43352064d97b944033b30c1d57cc506ec0", size = 90672, upload-time = "2026-09-16T00:15:40.306Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/2f/4b79940908c6ab8c795097c102999d7bc1f7e0b8604dfd1c232f9d99d67a/propcache-0.5.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:886b59c4d28ca97dd23b025fdfc50a0356be934efbbbca89ad26230067f86fe5", size = 52586, upload-time = "2026-09-16T00:15:41.575Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/07/02196ae6320c110235bb343f90dbd34be41f8b8964a3ee30db84ec12579e/propcache-0.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fa15757fea1dfcd5b7745cad9f4638929605531bd4018ab2adff7955f1a403d", size = 54335, upload-time = "2026-09-16T00:15:43.027Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/44/f48b9a131985659924df5fa5093f68fe72c7ee375329802989ba3126efc6/propcache-0.5.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f0093ac3e9daada202c2082439d414a625c57184727a46e112a3fb2a81cb788", size = 297567, upload-time = "2026-09-16T00:15:44.373Z" },
+ { url = "https://files.pythonhosted.org/packages/04/a1/418d956d2735139f77fc35262179f1f52c23aa666de5a8ab3819c1ae7854/propcache-0.5.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3cd3a7edb6b95b9b33998135ebfa18d709da82290fb8f27c858970b5a12c8b56", size = 297477, upload-time = "2026-09-16T00:15:46.048Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fd/ff811fdb6d3d3e67fd9bbfb75881675d34a42d0ef29a45d33e3e233dde07/propcache-0.5.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c174bfd1c48a1b51a3078e95586dde718374bac79719ab3541ec9e74aec40574", size = 302669, upload-time = "2026-09-16T00:15:47.458Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/57/527910c455b5ec62f6871bef45d4f79fea16cb8c966ba0d4a07f0339ddc4/propcache-0.5.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a219f0ac59817a9114dd2aa57c13180f993e819ba658c7ddab4b66ed1ee0d370", size = 287908, upload-time = "2026-09-16T00:15:48.99Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/86/f69ab82707534a0cb2057bdca04f9200a71214c7551800f9d34d6ac39e4f/propcache-0.5.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:17a7400cec0256f0a71ae71f9da398f9894c956ff6668a1c9d317b3367316320", size = 249804, upload-time = "2026-09-16T00:15:50.486Z" },
+ { url = "https://files.pythonhosted.org/packages/27/19/60677af50d93be4256213de7cd487f056944c048b9c0b6f2e45b3a30f666/propcache-0.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:978f28401afbc76cdc3df9e1717b4229a06b626a1dcc75db4e1f2beb3884c3e9", size = 282344, upload-time = "2026-09-16T00:15:52.029Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f7/a0057808a91fb3b6a5f3602b528f0cdcb3d53e0ff8315d73fabdfdf8fec4/propcache-0.5.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4a1f4f5ffa55dce6307631f3cb2948e117e665966ea512e0d502b16c24f567e7", size = 270167, upload-time = "2026-09-16T00:15:53.466Z" },
+ { url = "https://files.pythonhosted.org/packages/83/c8/f4a865490df0dc0c8531d4e59ac411cb6dc24bb255d2396a6f1c60a368f4/propcache-0.5.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:213bb68d9ced5cf2bf717b1071bf2b09b4b04c426256f9fe6d054c60318424c4", size = 286551, upload-time = "2026-09-16T00:15:54.995Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/67/b4faebde9da4e8173d0e5a30e8cd31335914af7ef350b988f27fec588cfd/propcache-0.5.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:286867fb156488c251a3721766e380ac4495e4fd6b51aaa1403d89ce7f4359d9", size = 249595, upload-time = "2026-09-16T00:15:56.505Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/40/52e1dd5636e9f5a27f6b5a4b4e2f33c322fd72afe956c397d82523ec4a80/propcache-0.5.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:445ee3bfb46e85838387fb3c536a73cc0b994dc192b004e40e170adc54aa2a7e", size = 286700, upload-time = "2026-09-16T00:15:57.985Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0e/30b2b324b93ff31a0bab539c102aae59e84e444031b2742150a7646aa1bb/propcache-0.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:48cb48c5346a97de792254af77715aa2529c2a1ebc5f586aa0aae44a02f1fe57", size = 280500, upload-time = "2026-09-16T00:15:59.487Z" },
+ { url = "https://files.pythonhosted.org/packages/64/36/721bb59f682ff060d0c8df64274fca8cd0521b1a54506c2eedaef795b7f5/propcache-0.5.4-cp314-cp314t-win32.whl", hash = "sha256:03b229037d25b801e7af53fd52b9fc49d9439b036fca1e087e02780631adfa97", size = 46121, upload-time = "2026-09-16T00:16:01.349Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/86/0b1b80fa1ac3a0aac44e2922a6964fbe9cd52af5eab8fa933bf9e90b030c/propcache-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:8a1fc236528c457cd739c88abe823da851b7ab645d72792f88658114cc340c12", size = 49154, upload-time = "2026-09-16T00:16:02.901Z" },
+ { url = "https://files.pythonhosted.org/packages/69/4f/9fe6f05a47cb550c823155052116f710064b6be5c6e8ec4e9faae7e18115/propcache-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:135036c5cfc93864affb0f9af9a27e5d7a71cb7bd745e7b6dbfc2d56cc30e827", size = 46005, upload-time = "2026-09-16T00:16:04.266Z" },
+ { url = "https://files.pythonhosted.org/packages/58/25/895a11d1e4c5c2acc6d816e2bece34e02d9dc92f2182ae276cd819e9e804/propcache-0.5.4-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:45bf2e730ab8905d0527fe05a86500f406e64305c34cc81ebe64b4617cab9760", size = 85634, upload-time = "2026-09-16T00:16:05.599Z" },
+ { url = "https://files.pythonhosted.org/packages/58/41/c0acd69271de7a1cf439e77d5d60c18575fd09bad56e798b95fa23458ea4/propcache-0.5.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:31eb43ba2edc704ab2ec27815315dd8a19def0fb16215be4cfe8d32fe78ffd51", size = 50084, upload-time = "2026-09-16T00:16:07.384Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/1a/ad561f99f90884089e6403b76c220610809429ba868a81a2e7ce115d32e0/propcache-0.5.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:174507f82d3594622acb1dd2dafecf2d899d6d506335494e7107767bf05f3aae", size = 51692, upload-time = "2026-09-16T00:16:08.956Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/07/057bdd3a9609ffad59b06239cceee784b047f6c720247bfaa36d2103e138/propcache-0.5.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e337653721d20ead710da33bf44487fbe8a0db8782714b60306481e9f95b51", size = 232947, upload-time = "2026-09-16T00:16:10.466Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/dd/d36ad35986718530498a65e45e3713f9f0e6a580f192ef02d2ef7cae9b52/propcache-0.5.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d21d0d2c82bbfeb1677a9711f38df968f9837576102bb4add1bd449d28d88f1", size = 241250, upload-time = "2026-09-16T00:16:12.056Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/81/f1459415cdb6c10d46942779de39bb59a77b38e5a76bb1def9227962eb45/propcache-0.5.4-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccf4f7a79e26bb7efb06ecd50c177833b71df05cbc748701372325e6bcc17f6f", size = 245150, upload-time = "2026-09-16T00:16:13.596Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/4e/58b9b1460afc97a4c0b17ee89af701c4011d4d7f46470eba3aaff76a8069/propcache-0.5.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23278f808cd81d5ada7184a76606b925fb3389c60e1077b2cd7da7b1fcf0553c", size = 232166, upload-time = "2026-09-16T00:16:15.126Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/c6/5a79e0eda3e7b6987d03d8c622ff6d52a42165a12e8418eb37694b9cc4b4/propcache-0.5.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e738ab81179510ce79b2eac9a6ecf47feffd9e76d1c72e403005dddb6e36c06c", size = 206085, upload-time = "2026-09-16T00:16:16.713Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/72/940aed42c73f9da345ca2de0f6e835c726498159abca5f1ef14fb0a2af8a/propcache-0.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a419ee85e654927baabda3929c03c0cc1112bf472ff0dfd6142f4e3a81ca4162", size = 228460, upload-time = "2026-09-16T00:16:18.352Z" },
+ { url = "https://files.pythonhosted.org/packages/85/71/3f54e1535c8f323d91ba566044d7c2b39ff6f6a2f1d0bd9071779d07b9b3/propcache-0.5.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:b61805357d966680acf68b3b6d49772631ed9df44ebece10ff1460e117a7da8a", size = 218350, upload-time = "2026-09-16T00:16:20.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/f4/025890cc389ac3ec485ecec607d4a7ca47e15bfa2a465746ab98af602536/propcache-0.5.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:58134228927cee6c047d626c08e60a81be604a20578a12ce752cc5c9a84d4826", size = 233156, upload-time = "2026-09-16T00:16:21.624Z" },
+ { url = "https://files.pythonhosted.org/packages/04/29/b39cae08c87c140d3d274f0a2c058cb5588e836175c3309e260b230ab07d/propcache-0.5.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:350b272b2279f4135a64fc0c304a5d08e28a137c9573442c606152446638a831", size = 206206, upload-time = "2026-09-16T00:16:23.204Z" },
+ { url = "https://files.pythonhosted.org/packages/18/61/e16462ef18a87247dc9ebbd5c606f46d5ce67e708bd9cc734dd0d9222564/propcache-0.5.4-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:45bebbe252550fec975ba3b62bc6f931643cfd3b5464ef47619cf3fef154e01c", size = 234469, upload-time = "2026-09-16T00:16:24.841Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/84/b6a1490922427204fc47df920ed002eec709621de6b79b11592bf45c623a/propcache-0.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ada748108a43d29b7c328ba7db3755327cd94f028bcc1a7ee3f0addcfacd9c38", size = 227311, upload-time = "2026-09-16T00:16:26.549Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/5c/5a59527582e9bcb694b2f08b9894134b65a0f5f79dbff174f054f5f74ed0/propcache-0.5.4-cp315-cp315-win32.whl", hash = "sha256:ee19113bce2f3acd46432050688b70f61acd6857d75abb9ec96341b7e9ced123", size = 43512, upload-time = "2026-09-16T00:16:28.313Z" },
+ { url = "https://files.pythonhosted.org/packages/26/07/93cf699ed363681e754d7c3fad587fb09ef6b65618ee193332ad16a68d7b/propcache-0.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:ceb3e879afac028f93d272c957814695dc5569e4904262dbee92f6c41bd5e4a3", size = 46264, upload-time = "2026-09-16T00:16:29.751Z" },
+ { url = "https://files.pythonhosted.org/packages/65/10/fef04fbdcd44a4a163cb5ff5674599c6d6fdefd64a5a459438f9ad2ba042/propcache-0.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:c83acbce9f2b5e3f5f5eda9e53d2001fed22fcdfef81274a9e02d8fd53b70a30", size = 43395, upload-time = "2026-09-16T00:16:31.5Z" },
+ { url = "https://files.pythonhosted.org/packages/70/f6/7e2f4dab0b92ab46111bd48cee9ee1e5f519514c44e3779ede5358d7ada0/propcache-0.5.4-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:a5e8ef588c109725dc713ba69aadcac00a1ef90c2ce9c0a8c7075128f569f47f", size = 89825, upload-time = "2026-09-16T00:16:43.115Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8b/dfeff925cb6ced97ede701d5c6a99998da963c6f2e06abbf879c9dac5b54/propcache-0.5.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:4d86476a935c88963d9b8e1a9a0d38188790e9622169bfbafa173046846709d3", size = 52159, upload-time = "2026-09-16T00:16:44.754Z" },
+ { url = "https://files.pythonhosted.org/packages/24/6c/924c810be5b7cf218ef47e707cf06d34adb4e3f3a31e3c24c55c6d945a88/propcache-0.5.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:f5470694918830da62fac9e69133b53d23b736d7070e587b27a4a2be37e08e68", size = 53956, upload-time = "2026-09-16T00:16:46.762Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/b6/9ed0a5c939b58b6bed740a05b5d0f919f0b318d03284b4b6d81a0fe8a29a/propcache-0.5.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10ef33a68a61ce317e095fd2e202a592ea92392b90944a78c993f0d9a73ab06c", size = 295235, upload-time = "2026-09-16T00:16:48.577Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/eb/5ce886e902a2e781dddf110993d5329458a9b1a8626b876c65e5e25bf413/propcache-0.5.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5cacf3c9efd09df409dc33654dd077e1c245ba8fb747b0f0236ef41b7c49b589", size = 294463, upload-time = "2026-09-16T00:16:50.539Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/88/c98f49183ecd3e5b204a556f0ca47baa02c2206a500fe8c7ec1726297b0a/propcache-0.5.4-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:770e8209d018175fc0063936fa9583b6d27e88c5ad31543f3383d66080efdd62", size = 300081, upload-time = "2026-09-16T00:16:52.423Z" },
+ { url = "https://files.pythonhosted.org/packages/27/0d/c5090f9e6f67cbc30a2b744c7bb0f8006dcba5ec1b0d82f866ae1cc7c5c4/propcache-0.5.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03969626faf0783a592dfa17e28eac06018bd0b44dafae6943d53b92421a7f72", size = 285360, upload-time = "2026-09-16T00:16:54.141Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/9c/34a55396910583ed07926669ab309dde2213a2dec05a7e946bb90ad66908/propcache-0.5.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef3b928d9c984322b5c44e6964d8dbc653da87d2d8ee1647fa6da43072e650a9", size = 248014, upload-time = "2026-09-16T00:16:56.062Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/b5/c0a142b656093ca397039dd3fe166cbb87c945712b534546514a24cd2611/propcache-0.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7177c43eddf10a0893c4fec52ebb408fdcd7f7d63962caace9180d8f81b14ece", size = 280662, upload-time = "2026-09-16T00:16:58.044Z" },
+ { url = "https://files.pythonhosted.org/packages/54/28/fab2809c2e337fe26becea9648e84d5cef46075c91b826acb13e4f9dd04e/propcache-0.5.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:420162a77f94eb1cf5ef7893f500016dabd548e73de956785a1dd899cc73006a", size = 266149, upload-time = "2026-09-16T00:16:59.702Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/11/7ddf336288b2678a5f054f8da2e2bd1a719f5d4b7de714d9c6bd588a2313/propcache-0.5.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:3eb2e820e8e2101407da93f17c57cbb7d225461955fc60105daaba14cd421ee2", size = 283097, upload-time = "2026-09-16T00:17:01.459Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/ae/351b1a5225f5473c411d9a612a229ae147cf0cf65c72ad838b87219ea8e8/propcache-0.5.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:13e52b6e0bde97dee98ab66552dbff2931649c96f1ac432eac299fe689ec373b", size = 248160, upload-time = "2026-09-16T00:17:03.298Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d0/7f79f061e30d135bb615c9782c94a74652033d00b49254edbbf35a9165a8/propcache-0.5.4-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12682126712ddc19b70ff819debbd279e58adf1f0c8f8f8138c18ade2044b284", size = 283036, upload-time = "2026-09-16T00:17:05.238Z" },
+ { url = "https://files.pythonhosted.org/packages/53/3c/016f1cad8bf4c428d748cf399b2bac603026fbfd6966e6a5579b5c5b6956/propcache-0.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:3af0c8642b2da4815d86e631232ac8286e17644fad907c19508aa8e7cb4ba8ad", size = 279350, upload-time = "2026-09-16T00:17:06.881Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/60/d8f72cb24b412487ed4c397f539117d3b74c3c33dd32020e91fe00a958a8/propcache-0.5.4-cp315-cp315t-win32.whl", hash = "sha256:1df8d8561b21465c5dd56110a01caf897e026d065b4b84e98a488209094272ec", size = 45874, upload-time = "2026-09-16T00:17:08.567Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/ef/8bae0a316d406644450522f2f3d44a4e19632f5f3bb60d1d0e6c53842616/propcache-0.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:02c0a34f16889cf800f10f0247a564d8ce6eeab6ffcd7c87198f769067eb8432", size = 48574, upload-time = "2026-09-16T00:17:10.077Z" },
+ { url = "https://files.pythonhosted.org/packages/57/be/bcc053f66a97355683884b448198e79580fae8e8fa4d96b9bb01614e9913/propcache-0.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:dc4242ca653c9b30ab51c5f8193323e7bc0928f897ee9103201e59a43abcb72e", size = 45625, upload-time = "2026-09-16T00:17:11.377Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/cd/785c64ed382f3f04201870267b02783f63b4678c2acfddc177a3ebcc2727/propcache-0.5.4-py3-none-any.whl", hash = "sha256:62c60aec739ed00124573cce1178138fd690c7676352d67a37328c1cf51d7468", size = 16338, upload-time = "2026-09-16T00:17:13.106Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "3.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/da/a8/c5fdbeee588bb8ada9458774f43adf1bdd30bd59157055142183e769a024/pycparser-3.11.tar.gz", hash = "sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc", size = 113796, upload-time = "2026-10-09T12:56:59.539Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl", hash = "sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80", size = 51178, upload-time = "2026-10-09T12:56:58.131Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.14.1"
+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/7c/0b/8e10b2e693af8ec54346a14caf36221334775975a747c9ceaa3f8371d96d/pydantic-2.14.1.tar.gz", hash = "sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26", size = 900362, upload-time = "2026-10-11T18:37:55.396Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl", hash = "sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454", size = 488106, upload-time = "2026-10-11T18:37:53.437Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.50.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a6/24/af4ec4be49fbc810f35b0bdcacc3d433b56bbfa469fd3bfd4ae116cd9bf1/pydantic_core-2.50.1.tar.gz", hash = "sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09", size = 513515, upload-time = "2026-10-11T18:35:44.82Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/09/db/c9cc34707ba2c23ce90d54e8a4ee9307583ad054b9df2ce570ba550cb576/pydantic_core-2.50.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b281a3b0f0822618fe5e3e0d8a2048b6356b14388505dc9374ccffeb69989713", size = 2154222, upload-time = "2026-10-11T18:31:38.345Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/6963f4ee55b7df7658f5c036c0f90baf8cd3d413f5d8b551f227e8467afc/pydantic_core-2.50.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87", size = 2048126, upload-time = "2026-10-11T18:31:41.061Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/be/f75637e222085d29a3076edbc5cfd0dd9b77d145e310ff7dc1150f07558a/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0", size = 2052460, upload-time = "2026-10-11T18:31:42.321Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1e/3d192ffa39ce3bd0e98e988fb5c055cfbf4af6e6e155a4ca5baa86d1f2cc/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de531ce1e2a3364e8767878b58f4ff728a434b4fde089781fe30b1e08e2396e0", size = 2190750, upload-time = "2026-10-11T18:31:43.572Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7e/12ea94df5220ab316ee5bb498ebcf710cff2db352653039fb6033f6997d1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c58106de36ac6a56314182958de20db8d3a29dfd5db527192cc754e4f8e7fb", size = 2390043, upload-time = "2026-10-11T18:31:44.745Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/bc/4fbb6dc90af038628ec824434a9841be9d46854f9267bb658f09e315024d/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b4c3df25bd323bf1d36a648d563cf1fc69d717451569927151bdad7cad07a77", size = 2432397, upload-time = "2026-10-11T18:31:46.186Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3", size = 2160719, upload-time = "2026-10-11T18:31:47.557Z" },
+ { url = "https://files.pythonhosted.org/packages/73/6a/d5b2c5876162fd2c444eaed0064b5d9d9d0ef0d31530fc6a7997e8685ad8/pydantic_core-2.50.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:d939de9c82e2126f7f48a7e658f8a85ed46d57662d53f44c49b8895fe94a3eb7", size = 2256164, upload-time = "2026-10-11T18:31:48.797Z" },
+ { url = "https://files.pythonhosted.org/packages/af/e8/1a6625d6e99250388dd0ae817c5d75839cd6103e7b790f59e0ffff74955d/pydantic_core-2.50.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:30ddf019d082c117b5d309e5b86710c2a78909907ec1a9381feec3eec02eca0b", size = 2271176, upload-time = "2026-10-11T18:31:49.987Z" },
+ { url = "https://files.pythonhosted.org/packages/19/9f/bbdd137d7ec41284701dab4d1489ea597d401dff2f5f3e6b41ea872e0b12/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2ab756b72bd5054e4c7ef3ded331b35786cbd3cf931531a508f79a9537517064", size = 2304637, upload-time = "2026-10-11T18:31:51.251Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5d/7bd322e649b41b45130bc00bfa34dc57498c853aa8e6fc9604e33bc7087c/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:b087b1c5be7ac687cf22eabfe4b6b608d40df23610651e93611e1f49118baf84", size = 2467374, upload-time = "2026-10-11T18:31:52.587Z" },
+ { url = "https://files.pythonhosted.org/packages/53/48/41a91111590eb7ba68a7c64c03139235a4b8349930abcb87931aa80da796/pydantic_core-2.50.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a44101320cfe99432db74237545a63057dc7a88dfe792cbcad0647f2af56cb81", size = 2480832, upload-time = "2026-10-11T18:31:54.06Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/02/4ac93a3d3f0ff412df1a36a9619743336fe6eec68c0df8a914163a3d22b4/pydantic_core-2.50.1-cp310-cp310-win32.whl", hash = "sha256:a4aaaa791bdae1c972a7e81765f4f3571c926b8e0b9b6e47346499fb80079665", size = 2123154, upload-time = "2026-10-11T18:31:55.396Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/ba/3a7af3dcd480c4739aa82cd9fedee79a5531a7c553346abcf095bd05f0de/pydantic_core-2.50.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eedf82ee4753cdab8e50044c6bd569577eebc3859b11fecf4eb9223761ff966", size = 2179366, upload-time = "2026-10-11T18:31:56.697Z" },
+ { url = "https://files.pythonhosted.org/packages/45/1b/ca81811da3d2aea724fb3c4ba6b83f50ab8f210576306ccd601973142d35/pydantic_core-2.50.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d", size = 2152145, upload-time = "2026-10-11T18:31:58.046Z" },
+ { url = "https://files.pythonhosted.org/packages/59/20/938ef7e0541af0a314e2f9d63848dbd1656ba2ebfe5ca0469f7ebee00d5e/pydantic_core-2.50.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71", size = 2045761, upload-time = "2026-10-11T18:31:59.35Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/4b/456497fe2affa18f49b226e89bde31e8a204751ee343dd9beaac5438123e/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350", size = 2049557, upload-time = "2026-10-11T18:32:01.121Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/9d/cd5dfe13928f55e236dff3a1bee06721d318ca6c955d9ca993b0a5d09f39/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1", size = 2188153, upload-time = "2026-10-11T18:32:02.442Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/8d/d0b25bc484f8d9136ea76ece69e0b2d24ab370cc2e7bbd1b79e61723b763/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63", size = 2388308, upload-time = "2026-10-11T18:32:03.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/64/6513cac7c9f78492abafa01d57a69de1d246bf3085fd7b0330bf14b025b3/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5", size = 2431187, upload-time = "2026-10-11T18:32:05.159Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/60/7f2ae89c37e69a3dc0da9ff57b0388c285542c688d05f36767770429de85/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133", size = 2157623, upload-time = "2026-10-11T18:32:06.616Z" },
+ { url = "https://files.pythonhosted.org/packages/87/02/e3a71373b85a7973099b9a229a4302c7792262717e0b4997e41c01f7dd16/pydantic_core-2.50.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253", size = 2254539, upload-time = "2026-10-11T18:32:07.968Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d5/5b620aaf40dc631d911565b64d49d3fa8422027c046a7a96dc4e4921ae98/pydantic_core-2.50.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b", size = 2267344, upload-time = "2026-10-11T18:32:09.399Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/48/f46baf4580d8a56ca4782aa65842e16d3cf39736b29fcce8e9c0521b3422/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae", size = 2300877, upload-time = "2026-10-11T18:32:10.903Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9d/e7d28505e6092c1fe075838a4f793ccbe6dd676e44ab16175815ee06cfe0/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef", size = 2464270, upload-time = "2026-10-11T18:32:12.329Z" },
+ { url = "https://files.pythonhosted.org/packages/22/b1/3bf63b82efd93ebc9968f9c12fc46037a28bcb452c88e4688f33e5ff7dca/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980", size = 2478653, upload-time = "2026-10-11T18:32:13.735Z" },
+ { url = "https://files.pythonhosted.org/packages/28/17/26d1e4b9c2ba6a92b7dc75bbcb99ef44456b7e3af1bced868403667ea7d3/pydantic_core-2.50.1-cp311-cp311-win32.whl", hash = "sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2", size = 2120246, upload-time = "2026-10-11T18:32:15.591Z" },
+ { url = "https://files.pythonhosted.org/packages/24/65/3e875e7991eaecd4d70686293ff0d8a99aa8f93dc295e6a21c3d702912fb/pydantic_core-2.50.1-cp311-cp311-win_amd64.whl", hash = "sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc", size = 2176332, upload-time = "2026-10-11T18:32:17.153Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5c/58dbd17c61f209d91c70f549eb7a3bbec7023e89ea12dead091462d6d658/pydantic_core-2.50.1-cp311-cp311-win_arm64.whl", hash = "sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e", size = 2182471, upload-time = "2026-10-11T18:32:18.49Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/94/101b49a6c63f99586755690696eae14532772587d9e39e93f7b8d28a49da/pydantic_core-2.50.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0", size = 2222428, upload-time = "2026-10-11T18:32:20.228Z" },
+ { url = "https://files.pythonhosted.org/packages/db/a3/f8b1b39349c33037c73d7a1b794c9c29d8f07d5bd2b74399c1375a529cc6/pydantic_core-2.50.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e", size = 2025779, upload-time = "2026-10-11T18:32:21.723Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/2d/862a7d115305cc621358e51ba13f296e0ffdf4fbd984be9f63bc7862730b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41", size = 2048429, upload-time = "2026-10-11T18:32:23.3Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/7d/fed95d18bf2b5abe2d5ab2bbec4184bfc22b6805994a33ccdae8edffd25a/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3", size = 2173274, upload-time = "2026-10-11T18:32:24.697Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1e/c6f22ceac65dc3d291ff79bea0eb74cbd1de8b128f01fee48af03bcea1e1/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5", size = 2360403, upload-time = "2026-10-11T18:32:26.281Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/97/190b5b6bfe4c72f265feb657913eb30749193927a5ad7b83cfa7866a25a2/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6", size = 2439799, upload-time = "2026-10-11T18:32:27.844Z" },
+ { url = "https://files.pythonhosted.org/packages/90/24/48cd98388e4ac2c08d0800af36d15413db15e20387f1038123a2c86cb2f6/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e", size = 2160157, upload-time = "2026-10-11T18:32:29.553Z" },
+ { url = "https://files.pythonhosted.org/packages/00/86/d909133d0b9979ed14c20f110f21124a0e346caf071bd66ac5af3009062b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a", size = 2229523, upload-time = "2026-10-11T18:32:31.027Z" },
+ { url = "https://files.pythonhosted.org/packages/90/31/4b43f08131b2098e73a852fd932063385f3103b33839fd9d1abdb89e673d/pydantic_core-2.50.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9", size = 2242700, upload-time = "2026-10-11T18:32:32.622Z" },
+ { url = "https://files.pythonhosted.org/packages/88/77/de39658569dc7a4c293d110a89629f61145e7538f00d1e4280c28debefd3/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff", size = 2278452, upload-time = "2026-10-11T18:32:34.165Z" },
+ { url = "https://files.pythonhosted.org/packages/46/bd/1eeb9e4e791e81210ef9096f58080b32ba15b5b13c108d50aaa3f8ce9f73/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4", size = 2450098, upload-time = "2026-10-11T18:32:35.547Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/e6/b2accfdff17e5a88d6358536702146de00f9339adaa4d5bbe1ee93cb4832/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2", size = 2476839, upload-time = "2026-10-11T18:32:37.119Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/7e/c8a83eb5d2cd42c2e13507150fb777b1edea5a018adc9cbf2858a1d08285/pydantic_core-2.50.1-cp312-cp312-win32.whl", hash = "sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e", size = 2101425, upload-time = "2026-10-11T18:32:38.593Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/17/8bbd530b8659e9d963f5b16f6cb8e159f4cd174b4d6304e48969ab12dba8/pydantic_core-2.50.1-cp312-cp312-win_amd64.whl", hash = "sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498", size = 2172522, upload-time = "2026-10-11T18:32:40.112Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9f/a79d690b127e4e3cff143d031538ab9aa715f6b2fbeecbd07cd6f2bf9fb7/pydantic_core-2.50.1-cp312-cp312-win_arm64.whl", hash = "sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1", size = 2161366, upload-time = "2026-10-11T18:32:41.606Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/57/0e237d7091d2cd44a35d243b7344227f90440166860469cd036afc23a04d/pydantic_core-2.50.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b", size = 2222774, upload-time = "2026-10-11T18:32:43.103Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/b9/c720e56858d4e1539503297ed37063e0c08e0f3541c41b777f6a800f75fa/pydantic_core-2.50.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482", size = 2024738, upload-time = "2026-10-11T18:32:44.587Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b8/fbfc25875219cc060e613170ff10e850c6d8924beb4910da57c2ee3ba1d2/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d", size = 2047049, upload-time = "2026-10-11T18:32:46.164Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/43/34210124d504c553688f2f04b48500b131237528ac545b445e0d6d30e0d5/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658", size = 2173415, upload-time = "2026-10-11T18:32:47.722Z" },
+ { url = "https://files.pythonhosted.org/packages/65/cf/6e178e8fdc11da5965bef980983bf46326a42f436871dd51ba0a57f39df1/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4", size = 2361719, upload-time = "2026-10-11T18:32:49.217Z" },
+ { url = "https://files.pythonhosted.org/packages/11/14/bd5169d356aa91bf777e28ba0b281c192d286d03c2812c9c9db023a2f00e/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255", size = 2439803, upload-time = "2026-10-11T18:32:51.025Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/bc/d79d000e5203ebef39af839f2ce77a777fcad6e08ad26af9a2fcd114ffc8/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec", size = 2158402, upload-time = "2026-10-11T18:32:52.714Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/a5/4902cb5fd599422c130bd3124ab31ee8b771199bd56662702eed07d3fec7/pydantic_core-2.50.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72", size = 2229755, upload-time = "2026-10-11T18:32:54.126Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/f5/c1481f8669f6060d89110c9b1374173fc8767ca276b84f4870bd8acd5e3f/pydantic_core-2.50.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c", size = 2242303, upload-time = "2026-10-11T18:32:55.641Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f9/77fc3c7653ba9b6e42049e17e96b25388187d4a7c274ab1cb07f58ac8419/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568", size = 2278345, upload-time = "2026-10-11T18:32:57.38Z" },
+ { url = "https://files.pythonhosted.org/packages/95/9b/0579c5d12e7f2b16b27e6782427987341fcce07b0725e02ddb0b74add0c4/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8", size = 2449947, upload-time = "2026-10-11T18:32:58.896Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ac/1b677db91eba54cc5922f4de6edc46ba45c2bd712dfdc0130382d158e214/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4", size = 2476889, upload-time = "2026-10-11T18:33:00.665Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/2a/3a9f6624ee3ea9ccba5249dde11418bb4c35780a7a92608f8768bd3fea39/pydantic_core-2.50.1-cp313-cp313-win32.whl", hash = "sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685", size = 2101118, upload-time = "2026-10-11T18:33:02.329Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/1f/323f78ddd9d9938aac420c1abb4e8ba799fc8bdab0acc67c0593b837c979/pydantic_core-2.50.1-cp313-cp313-win_amd64.whl", hash = "sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f", size = 2170840, upload-time = "2026-10-11T18:33:03.919Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/09/8497c52a739ae425c3ac2f7f56414cbc711c67d374346174f40fe2062644/pydantic_core-2.50.1-cp313-cp313-win_arm64.whl", hash = "sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662", size = 2161633, upload-time = "2026-10-11T18:33:05.518Z" },
+ { url = "https://files.pythonhosted.org/packages/49/33/28b96e81677153715e3eafb9f26663a80841e859fde282a380359b0d3fa1/pydantic_core-2.50.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a", size = 2224428, upload-time = "2026-10-11T18:33:07.153Z" },
+ { url = "https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9", size = 2023740, upload-time = "2026-10-11T18:33:08.763Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c", size = 2049435, upload-time = "2026-10-11T18:33:10.366Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8e/f195aebec49ad12318876ac2368c197a7939f5d52f8e156d2238ef8a4588/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb", size = 2172975, upload-time = "2026-10-11T18:33:12.368Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/f5/7ad9fb83010cd5ea0948409db105676ed779c4e709e2d3d89b9fe2558794/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597", size = 2364197, upload-time = "2026-10-11T18:33:14.244Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/fb/bf0aab3e78301d202b82a0322ec968cd703b11fc0623fde9a28708936f62/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899", size = 2444259, upload-time = "2026-10-11T18:33:15.79Z" },
+ { url = "https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea", size = 2159857, upload-time = "2026-10-11T18:33:17.51Z" },
+ { url = "https://files.pythonhosted.org/packages/65/a0/fb0a3ca10f139dcf765b2d312d10cf0f65c57c59993229d00ad12b14ceff/pydantic_core-2.50.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e", size = 2233954, upload-time = "2026-10-11T18:33:19.591Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/6a/e63842252702aa4ec6e6b7178ca85e541a77459076592c23ebe2d9840b33/pydantic_core-2.50.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20", size = 2243876, upload-time = "2026-10-11T18:33:21.393Z" },
+ { url = "https://files.pythonhosted.org/packages/39/25/5991cf8318b37e0dfab47b87541619a1df8501a793cba0d978846cba37a7/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc", size = 2277908, upload-time = "2026-10-11T18:33:23.324Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/3e/3ee8baaa6cc25a6961c69168cf9ff0f002d56f4e0d541ad6724c18fb61f3/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396", size = 2449285, upload-time = "2026-10-11T18:33:24.942Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/acbec6deac13fe697a80c275a9b6661db62c4d323bac8a47347b1f39c7cd/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb", size = 2479013, upload-time = "2026-10-11T18:33:26.925Z" },
+ { url = "https://files.pythonhosted.org/packages/10/08/21a3f237b264389f6219d053ee78fc1b5c2fd402c1b1cb2b6cb8b85f9834/pydantic_core-2.50.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5", size = 1484400, upload-time = "2026-10-11T18:33:28.693Z" },
+ { url = "https://files.pythonhosted.org/packages/09/ce/077a6d262d12ef09108377ac0717f029f420d773ace63cafd6143af75568/pydantic_core-2.50.1-cp314-cp314-win32.whl", hash = "sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2", size = 2103222, upload-time = "2026-10-11T18:33:30.326Z" },
+ { url = "https://files.pythonhosted.org/packages/14/4c/350a2415209c43d670eb71d3c040f332c04a30583d39e311b05c7ac15762/pydantic_core-2.50.1-cp314-cp314-win_amd64.whl", hash = "sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f", size = 2171586, upload-time = "2026-10-11T18:33:32.139Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/92/9bea6ca96580a0902fed366f064f0829e404f41889b34543279a1162b888/pydantic_core-2.50.1-cp314-cp314-win_arm64.whl", hash = "sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea", size = 2155773, upload-time = "2026-10-11T18:33:33.896Z" },
+ { url = "https://files.pythonhosted.org/packages/86/8c/f121f073cf32bdd5cba7e6230b1ce0ac845b0cf43e44dd597d95af272db2/pydantic_core-2.50.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070", size = 2234017, upload-time = "2026-10-11T18:33:35.588Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/69/2bb2bcd6146cffe98d760a46c42ae71efd5151d9b2f9c9bf6619a3b32083/pydantic_core-2.50.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e", size = 2001016, upload-time = "2026-10-11T18:33:37.57Z" },
+ { url = "https://files.pythonhosted.org/packages/20/b3/fbf854c7d07ec114260c26e9e2071a4381740f9ae09641dbfcbdf2a18c45/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5", size = 2023015, upload-time = "2026-10-11T18:33:39.433Z" },
+ { url = "https://files.pythonhosted.org/packages/65/20/6de55b2f92cdb614b745c6e9ced639fc4fd7e1e77604825a88bdece6fcbd/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb", size = 2192543, upload-time = "2026-10-11T18:33:41.381Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/d9/19e91c94bd5c405945ce2f15526808aea37e162c253160de8ed7bf70b406/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e", size = 2388704, upload-time = "2026-10-11T18:33:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/bc/2e24c8415eae1a25ee5a5484946a917123bad2e9d01689a759236928175a/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b", size = 2464118, upload-time = "2026-10-11T18:33:44.856Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/1f/945b8053cb061c64e102bcaf7bfb9ed740c0bd4349f9bb978a7d4ddff4ab/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019", size = 2150142, upload-time = "2026-10-11T18:33:46.661Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/78/96a3e50bf0d64aaae781107eb9335b7529d05a85793ed6e7241c4cd1d931/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112", size = 2245577, upload-time = "2026-10-11T18:33:48.574Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/5d/a6038a0322232758a6ebfa709f4ca14a60bf5b93940cd0b345056a557da4/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a", size = 2263728, upload-time = "2026-10-11T18:33:50.527Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/4b/76ded3333a457a9344c4f2d63f2d82d0101654b05178fa4ddfe7d3627674/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb", size = 2293630, upload-time = "2026-10-11T18:33:52.362Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/00/9eca378335c9c1b72bc779bf6e4c2a82ce784f14ec48a0e87102c9870e05/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b", size = 2468048, upload-time = "2026-10-11T18:33:54.118Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ca/e3832e9cf93651251de43c8c5c029ed680a3c1a0a1b17ee26c81bed08cbd/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807", size = 2512159, upload-time = "2026-10-11T18:33:55.99Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f2/773469b5a10a39116a2cb17edaf6d722f017dd8da07161b371465311c542/pydantic_core-2.50.1-cp314-cp314t-win32.whl", hash = "sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19", size = 2098406, upload-time = "2026-10-11T18:33:58.043Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/42/0bb74f8f25204b259b11ab7c12dc7f180893b6bd706118b385147fb6efd5/pydantic_core-2.50.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c", size = 2148481, upload-time = "2026-10-11T18:33:59.913Z" },
+ { url = "https://files.pythonhosted.org/packages/47/0d/d801646c9679a4e630e15cf521d4108b93854b42a6e6e02391bd4c6b1095/pydantic_core-2.50.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86", size = 2174972, upload-time = "2026-10-11T18:34:01.875Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/84/23984b763d8862a02a13d27a44b6e8169428fd85ecfed88f54c29108604d/pydantic_core-2.50.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102", size = 2224599, upload-time = "2026-10-11T18:34:04.016Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/90/a63cf8586abc1d1a3f6d9b18f0224789ebf003851f092ebda3c7c863fd32/pydantic_core-2.50.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c", size = 2024191, upload-time = "2026-10-11T18:34:05.901Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/6a/f34bff9808ffb4907fbf5f5040457d253cacf2da7fce9efbc99ca1b1a44d/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7", size = 2048252, upload-time = "2026-10-11T18:34:07.757Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/54/13f419bf1eb59852003818e25935aaf175687d978f4c4bca70e08fe40a3b/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43", size = 2173376, upload-time = "2026-10-11T18:34:09.592Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/6c/b5a34d24cd0c81669d8f8339d74e6815abcf2f8fb48ab4b49b84c09be1d5/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78", size = 2364559, upload-time = "2026-10-11T18:34:11.534Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/8d/d64d6216a8df365082665927ff923f183056f9049fee08e9777c9ac05296/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831", size = 2444697, upload-time = "2026-10-11T18:34:13.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/0d/1b1149f60a00ea21ba5f70e28acbd40feb4598af414f80f53c921fac07c9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa", size = 2159961, upload-time = "2026-10-11T18:34:15.56Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/35/f236549299dcc78e71e945495d7ec67e78d20844d0999c60601685003031/pydantic_core-2.50.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5", size = 2234581, upload-time = "2026-10-11T18:34:17.502Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/85/26901a490522b7f75ef9bb9a7afb73e5bb550f0e1cb5b99a0069183e2eb9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415", size = 2244090, upload-time = "2026-10-11T18:34:19.402Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/2c/481bcfc70ceeb77a778ca6e5b705fe592cb64735c108157f81b7dd9c280e/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10", size = 2277948, upload-time = "2026-10-11T18:34:21.317Z" },
+ { url = "https://files.pythonhosted.org/packages/24/eb/f1e09333faa7ba447cde967310f758430cc7c5e987bdab5228816c70030d/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f", size = 2449688, upload-time = "2026-10-11T18:34:23.321Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/b3/036bde636db8f76d92996e81aefc75678ab5cec4a07eea1ad0c72a893fc3/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459", size = 2479286, upload-time = "2026-10-11T18:34:25.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/a3/07f018294ee18d144afeb6df1a47d9e92960f014be45c5ed13519fa5af95/pydantic_core-2.50.1-cp315-cp315-win32.whl", hash = "sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290", size = 2103683, upload-time = "2026-10-11T18:34:27.42Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/98/f9bd7e1f9b6709f155acb9ef826d9c3884fe925f811fd8e55b9b52280bad/pydantic_core-2.50.1-cp315-cp315-win_amd64.whl", hash = "sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed", size = 2171681, upload-time = "2026-10-11T18:34:29.508Z" },
+ { url = "https://files.pythonhosted.org/packages/10/87/4bb3e1e7f385c076ab5af4d6dd0571b22042cd8207eb810d5b9fef15ae31/pydantic_core-2.50.1-cp315-cp315-win_arm64.whl", hash = "sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54", size = 2156251, upload-time = "2026-10-11T18:34:31.455Z" },
+ { url = "https://files.pythonhosted.org/packages/47/47/83643225b08f2aef6c8cc4bbe6e3f79c5e139c4364a6e450d0f399d87774/pydantic_core-2.50.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a", size = 2234363, upload-time = "2026-10-11T18:34:33.65Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/66/127ca649ba2f2462039dc1e694c6c01e00a3689f023a617364d3507e6d97/pydantic_core-2.50.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08", size = 2000972, upload-time = "2026-10-11T18:34:36.037Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/4f/e421e0a5d653b1203b090b2e48745976988d7338a647940704b5b9c2b399/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe", size = 2022925, upload-time = "2026-10-11T18:34:37.972Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/ea5568e2491e1a71100f15ae8c2d01ef52db42a184a2d4e716bc79e5eb8f/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f", size = 2192867, upload-time = "2026-10-11T18:34:39.921Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/5e/3b8c3a35acbe219909ada5defad5d7d9fed845fb2b37bd5ec518f453c119/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8", size = 2388687, upload-time = "2026-10-11T18:34:42.184Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/aa/7889b4e515f91a2e8c0ae6b5081fec0feb30c4398d1434e14793c60f173a/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a", size = 2464415, upload-time = "2026-10-11T18:34:44.384Z" },
+ { url = "https://files.pythonhosted.org/packages/08/78/93449e628eb8a6fdcce3eff9043081179d1bc7ce6f1bff32dc5006f41e00/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8", size = 2150823, upload-time = "2026-10-11T18:34:46.392Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f1/72c5bc129fceb0d00f05dc1e67f518c1728de1928c55f81fc13d7690de39/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709", size = 2245806, upload-time = "2026-10-11T18:34:48.805Z" },
+ { url = "https://files.pythonhosted.org/packages/28/2a/922a0e78f3aa6ab837b59f88190233fb8dad546c17995fde9bb3ed3b9b49/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2", size = 2264160, upload-time = "2026-10-11T18:34:50.876Z" },
+ { url = "https://files.pythonhosted.org/packages/52/a8/0f1449e3e1b20941c9372faa18e7b6a092e30cdfa841902473f7989532ac/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a", size = 2294031, upload-time = "2026-10-11T18:34:52.876Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/d0/1031f492857de70355fb16524bbb03efce5fd34c93ed4a1ec60be07e0d4b/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f", size = 2468423, upload-time = "2026-10-11T18:34:54.995Z" },
+ { url = "https://files.pythonhosted.org/packages/46/52/269ffffa645b8e47906395a39cf9db1151ae9fa4bcd7f47b960bc31baf37/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b", size = 2512598, upload-time = "2026-10-11T18:34:57.149Z" },
+ { url = "https://files.pythonhosted.org/packages/31/5c/e47e28281f20326ff6f3c31d626f0a83e615d2d94ba41bb0ad6ec237184a/pydantic_core-2.50.1-cp315-cp315t-win32.whl", hash = "sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f", size = 2098535, upload-time = "2026-10-11T18:34:59.313Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ad/759e181e69c1b472c60b2049e5f61d5da1deefdd5ce1df4bb2ebcf771f25/pydantic_core-2.50.1-cp315-cp315t-win_amd64.whl", hash = "sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b", size = 2148645, upload-time = "2026-10-11T18:35:01.571Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/56/8a702c27e5be9f47e5f19d8669227424290e4c024e7c370279cbaf244b4e/pydantic_core-2.50.1-cp315-cp315t-win_arm64.whl", hash = "sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44", size = 2175297, upload-time = "2026-10-11T18:35:04.079Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/73/a23a327237d9bb985298ce47702e196f995ce850224c12c70886bd66bb5c/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769", size = 2162560, upload-time = "2026-10-11T18:35:06.274Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b2/33b37b5e82408f402a5e82649a43e7c95cd5dab7beaeef2c35cedbfd18f2/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9", size = 2075250, upload-time = "2026-10-11T18:35:08.455Z" },
+ { url = "https://files.pythonhosted.org/packages/04/62/c7d5b9249466ccab90f3ce4790c2399dbfb8172ae9e74a7753dd295fe65a/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92", size = 2107891, upload-time = "2026-10-11T18:35:10.918Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/7e/28d29bc1657195933147f772dee0558d9e0f045bbc11db7f992f40c37bb5/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87", size = 2268026, upload-time = "2026-10-11T18:35:13.336Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/09/1bcf160f3cd6333e2a76982fae5963e5032de0bcaaba38464223dc266bfe/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242", size = 2217070, upload-time = "2026-10-11T18:35:15.688Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/81/f02de95eec7794dfc7a5bc69a834f0e834b31b3009014ce6de903855f775/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5", size = 2039027, upload-time = "2026-10-11T18:35:18.07Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/16/227746ed3771d9bf2304568b55134849ece34e13b38b1d76de763015c77f/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295", size = 2109009, upload-time = "2026-10-11T18:35:20.218Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/b9/7664d1592a0e5a74903f357cd5b28ceb7336f3473256c5a4e8c8432f1881/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61", size = 2269883, upload-time = "2026-10-11T18:35:22.48Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/35/002a378302ff91d2a7f149bc8d22363faf581ba40ac39d0123444315a32d/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84", size = 2154076, upload-time = "2026-10-11T18:35:24.743Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5c/7a034330082c00f3c9795052bd9b161aaaf6e1951e897ac00a094823ef7e/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f", size = 2085850, upload-time = "2026-10-11T18:35:27.009Z" },
+ { url = "https://files.pythonhosted.org/packages/57/fa/d5b44a7d3d4eceed6ef8713a0003af56fefde6ec75915513bcdd5d730cd9/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906", size = 2259754, upload-time = "2026-10-11T18:35:29.763Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/88/e01dd37301710bd8b3b2daf34ec9e2a6e83a333130831c0d468b0292a48c/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760", size = 2268712, upload-time = "2026-10-11T18:35:32.654Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/43/5bb80d4a4d2b2d84612a30205662cc521a20d209c94abc079ea48b4e0df2/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad", size = 2305049, upload-time = "2026-10-11T18:35:35.392Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/7b/e3ef76b269cfe4a5bd3bdcf5d7124cf05e5a8909d443b91639307116bdae/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe", size = 2466001, upload-time = "2026-10-11T18:35:37.791Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/7a/ea0947aadc1d9f72d4be00d2e19939611ec17363bcb5e6eb9ec7d8221c34/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5", size = 2481777, upload-time = "2026-10-11T18:35:40.287Z" },
+ { url = "https://files.pythonhosted.org/packages/24/f3/d15bc0b1fb0c4f1e07b3326ffb7489f7ae70da2b56a0884d22641b0eb47c/pydantic_core-2.50.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf", size = 2336787, upload-time = "2026-10-11T18:35:42.558Z" },
+]
+
+[[package]]
+name = "pydantic-settings"
+version = "2.16.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "python-dotenv" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/ca/7817b4bf0c4b15c6a6ab8cdf4cb96c268335a8ca2f525de93b6ebe4c7d28/pydantic_settings-2.16.1.tar.gz", hash = "sha256:aff602a1886338c2df3d1e958e2193a8dc33cec75b91f202e83fd98484b4b0da", size = 303436, upload-time = "2026-10-20T11:37:19.024Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/74/fdf926afe145d8629c4b2ff57f4ea1d64ed7abc4d84d97463b2517a2547e/pydantic_settings-2.16.1-py3-none-any.whl", hash = "sha256:30f5313340da4e8f1b440d1910b92a5bc3ccbb6b4ba70ec2b8395bc921f418f5", size = 80771, upload-time = "2026-10-20T11:37:17.298Z" },
]
[[package]]
name = "pygments"
version = "2.21.0"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pygments/2.21.0/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c" }
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pygments/2.21.0/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9" },
+ { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
+]
+
+[[package]]
+name = "pyjwt"
+version = "2.15.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/ea/5194e52748b0da83d71e082d75496eaec6e58f419f5e184786ded517e6a9/pyjwt-2.15.1.tar.gz", hash = "sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8", size = 121252, upload-time = "2026-09-28T18:40:42.598Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", hash = "sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", size = 33860, upload-time = "2026-09-28T18:40:41.429Z" },
+]
+
+[package.optional-dependencies]
+crypto = [
+ { name = "cryptography" },
]
[[package]]
name = "pytest"
version = "9.1.1"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
+source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
@@ -106,173 +2150,1065 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pytest/9.1.1/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313" }
+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 = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pytest/9.1.1/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c" },
+ { 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 = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
+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 = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pytest-asyncio/1.4.0/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42" }
+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 = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pytest-asyncio/1.4.0/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1" },
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.2.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/74/26/2fbeedb218a787a5eea551c7532cac4e009f83d689dd2faa0d0353473f86/python_dotenv-1.2.4.tar.gz", hash = "sha256:f0d53e69935a851c0dcc78f3ab7aaccd8cabef0b92382b576b824212902873c0", size = 60824, upload-time = "2026-10-01T05:36:10Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl", hash = "sha256:42269a8a5b3fd54ffa6f3d84b18abed50064717576b4ecf03dc4a55d8aa04fdc", size = 23266, upload-time = "2026-10-01T05:36:08.633Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" }
-wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" },
+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 = "realtime"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "typing-extensions" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/89/2b/9169d867f4dc0413bb2e493b2e262acd2fd91056aa7614c23a408f79d62a/realtime-2.33.0.tar.gz", hash = "sha256:e4b10077d59876a75070b7e72074945ea78b4a99369771f8b968f4092847c65d", size = 21172, upload-time = "2026-10-15T19:14:19.493Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/d6/4a5c8b6b12104816d9205732c1cb649f47e75367e36c06eba6bdd54e9b77/realtime-2.33.0-py3-none-any.whl", hash = "sha256:da94bb2e6e33859d1aece87656444498a1f7a5ca1c3484a84aa4a23f8940deec", size = 24158, upload-time = "2026-10-15T19:14:18.493Z" },
+]
+
+[[package]]
+name = "referencing"
+version = "0.37.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "rpds-py" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
+]
+
+[[package]]
+name = "regex"
+version = "2026.9.29"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/f2/af1da9d3ceed77bfcdce40427d49ba0be94e4fe84245e3bfef68c10e75b6/regex-2026.9.29.tar.gz", hash = "sha256:8b5fcc4771732191b2b7d1dd68d8f0353f47f8d90b6150f6dce58bf1112442cb", size = 419199, upload-time = "2026-09-29T00:49:58.298Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f6/37/404442e7296554792e2de4c0c8f8b95ee491a3c227d6ff90d4cebf2dbde3/regex-2026.9.29-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9916fda742cd4eede63b286f58c06718324265d727ce0856eb1aac86d0d150d6", size = 495402, upload-time = "2026-09-29T00:45:50.322Z" },
+ { url = "https://files.pythonhosted.org/packages/72/6d/116db2946888bd60db8e2033739471be6d80aa47c833afd952c9ed40654c/regex-2026.9.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8873c4a11c50b9989168881aeb3f08859f469d809941866aa1feefd8be5431f6", size = 295139, upload-time = "2026-09-29T00:45:51.826Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/1d/aefd12fb5cd62b748aa6ed28725b375c71102853fcbf15fb6481f806a2ba/regex-2026.9.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d9fe8091b2e89d470df68a9331111ed008ae8aae6bf1e8e1fba4086a495c84e", size = 292130, upload-time = "2026-09-29T00:45:53.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/98/41fe60ae6ccf3e166fe7bc73495aa15e9a1a7fe3a1f9f0f95a3d64a2df0f/regex-2026.9.29-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb00027a09a8f9f08028b40dce4c933cf73e4833240ed356583fdc9cfa721566", size = 793080, upload-time = "2026-09-29T00:45:54.331Z" },
+ { url = "https://files.pythonhosted.org/packages/45/0d/944b13e8286ecba61a3baeca28b375550821a279800bdb18fac41ade6637/regex-2026.9.29-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14e953ff3607c92d7675bf79c4d4509ef6782aa8c08509f179f9b3d6d0679e86", size = 865241, upload-time = "2026-09-29T00:45:56.065Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/b4/e1c32fdee0462f1373c52a73325cafbde653fac0b0cc1e9badac74154f4a/regex-2026.9.29-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0476e5bcbe6e1ba3d1c4cc7bbb1c3ba78e3b979b5c8a88d0a6a8cdd4992b8c84", size = 905496, upload-time = "2026-09-29T00:45:57.491Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/99/eae371ca63f7ea1f1eac025021527adb57ce3a4b67ff608b896fd31ea447/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fb41211d2333eb930a51e0546a65999761cf1f572a4da56ef9b8a62966c06f2", size = 797643, upload-time = "2026-09-29T00:45:58.746Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/26/9e4f158459e37e41633c96f49766a465f801c5daba9507260609ed76d783/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:edf06545875f3efa31560d94121e95c7fd70d98b1dfedc0157097d79b13b52ea", size = 797591, upload-time = "2026-09-29T00:46:00.2Z" },
+ { url = "https://files.pythonhosted.org/packages/86/60/5e1c5c6e85132e757ae77bd7693f1809046edb2c8853658c9200f4c8593c/regex-2026.9.29-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6398d5145689503412cc1748895242598d8846b8967b851133b20dc2ed1e21e8", size = 774874, upload-time = "2026-09-29T00:46:01.491Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/13/301b394f32a8db86ae3280533c9186229595fed766376f18e87959f92f96/regex-2026.9.29-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:45010bcfe66df41522d56c9b6114e87ecc597a08970ff6a2ced24415c141ae5f", size = 780168, upload-time = "2026-09-29T00:46:02.817Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/27/0c647127db32760b2dfc4c38da9ca509a3c4a8d3d93a586121a10fef2701/regex-2026.9.29-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5758353650079898dc1b2b0e95aa51fa23a30d020e06f62c430dd08ee56cdd8", size = 856604, upload-time = "2026-09-29T00:46:04.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/18/cac15fb2829c33218ececf76333b66869a96e4f973480ec32494921750f0/regex-2026.9.29-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6f7121a8914ed13fcfe2099f895341bfb789f004d4c5a0bdece8fa667da10849", size = 760602, upload-time = "2026-09-29T00:46:05.794Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/55/967676e4089a25d5f54b9d62522f9fe09b87677d1db58f207888d4ccfbe3/regex-2026.9.29-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b9d74e4eee9ddb64c2e92d5d61472c59c21684c059eb7b68767be9628e977859", size = 846830, upload-time = "2026-09-29T00:46:07.258Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8d/bb4165d61860ed443da5fb0fafe629c1ee9202526394eead7925bdee2be4/regex-2026.9.29-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:143533cc4b6fbc5b95aca0a5b8d541088d374831593def000ec89322c220221d", size = 785445, upload-time = "2026-09-29T00:46:08.89Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a6/a5cfc2560e063f8e6871373d3b719f573d7e7fcaa09db43e7c8aaeb51bd5/regex-2026.9.29-cp310-cp310-win32.whl", hash = "sha256:b84f186a7f0536fe4ff9a9fa12d06d007b9b71d4b5352ddcc41f59ad6522a312", size = 268950, upload-time = "2026-09-29T00:46:10.331Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/f6/b127312ebc8092357f435c9e4594fd8cf50abb85f8b4b16ab8d262abab2a/regex-2026.9.29-cp310-cp310-win_amd64.whl", hash = "sha256:23ae6fdad9e63e54038f5ef78aba2933faca61e24d432786589e737bc5522ebb", size = 280629, upload-time = "2026-09-29T00:46:11.638Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/e4/ab36f3c26f2374d16d525dd5a7fb4997f3c6e6bb7c6722821b84f9ea35b1/regex-2026.9.29-cp310-cp310-win_arm64.whl", hash = "sha256:c0094897d7d01f184b2d7fe8c56c66d64efe01b31f4b7d34205b391387df1111", size = 279407, upload-time = "2026-09-29T00:46:13.248Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/6b/6dea87689c3a06a6e79d254bf824e6f3e3d724b5ba027c6112559aa6cd2c/regex-2026.9.29-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6abb75ab16bc3281714a5b99548a2225db70dba1f995f6d7f7419b76eb5a8fbe", size = 495413, upload-time = "2026-09-29T00:46:14.51Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/a5/0c791a0e83ad1013d262c13247c4c77e0f4a8d05bdc167df96aba6681c0d/regex-2026.9.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b7b893976e7fe42053da64f2aa27239c24252fd2ec6df471e1be197c0addc3b1", size = 295145, upload-time = "2026-09-29T00:46:16.292Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/07/9bf3607d8d13a12e436ab9d63f9791e10706827d535695b23964ad79fd79/regex-2026.9.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:066d0e3dbfdd739bce2bf8c2a41dd16f73e3d8adc2eb06dd803a36a307f56075", size = 292123, upload-time = "2026-09-29T00:46:17.646Z" },
+ { url = "https://files.pythonhosted.org/packages/64/6b/32c2e6fc617e1d3f247e250fea31a9a35b1265bd32f585968aa13b9999b9/regex-2026.9.29-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7020ed44df30b3aa492c00ee3b52d0548c1f30c2c6c5bb13ae897680900d3413", size = 799537, upload-time = "2026-09-29T00:46:18.976Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/72/f041177f3c7a4606f7c81a95fe7eea03e2a0c4e8bff9e439a01432cbc9f2/regex-2026.9.29-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4613d7d9dda60fcba95f846cc6f808017f1843f392cf9daad14a6534493d71", size = 873255, upload-time = "2026-09-29T00:46:20.684Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/4e/a78948e11dd715e0e46716c2e0f3404b3fe6a44e2a2e9abdc7d965cab2b3/regex-2026.9.29-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec37990e3d6121f29ecfb594bd8f1bf009e9f7926daba2e50e3b27d3892a783", size = 914570, upload-time = "2026-09-29T00:46:22.599Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/70/aa08d1d2b294894b365e5f8ba5380fe3f8546acdb81f10639dfd74209c37/regex-2026.9.29-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:612b709381c0355b70d89cdb51b7f670591ed5cbbc0e3b5337488019dc667b65", size = 804978, upload-time = "2026-09-29T00:46:23.981Z" },
+ { url = "https://files.pythonhosted.org/packages/21/32/1b03534c4715aca3b564416d28d518083ed4dab3bc913267600d2256140d/regex-2026.9.29-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a760da040b47767b4b873adfb7c3b691e9ba2fc60f113f9d0b88f1a62f323e85", size = 779297, upload-time = "2026-09-29T00:46:25.318Z" },
+ { url = "https://files.pythonhosted.org/packages/76/a7/378f6f558d9e4444af315a307c5953565a511d1e3666f1bb7bdc82012b6b/regex-2026.9.29-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ee178ca31c94621294bf9b8b676a92a2e6bba8af0529591753719e57edb621", size = 786961, upload-time = "2026-09-29T00:46:26.963Z" },
+ { url = "https://files.pythonhosted.org/packages/59/13/79f0b1846f5f342f92ddbd4b27b18bcb86da96d902c1a0be26520bde98d7/regex-2026.9.29-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5eeb8edc6110d9194a4d0d54610f64c37a31c605b5dbb7e407fc6ec7fa34a4a1", size = 863234, upload-time = "2026-09-29T00:46:28.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/19/05af70dec9f2eed6ba34e08d2dcc6a48e7ae5e307659d5fe4201a5d7bbee/regex-2026.9.29-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ccb64d887a9db1cd76dbc0f92051a1a478a2a67e7f56c62d915cb881d7734704", size = 766487, upload-time = "2026-09-29T00:46:29.941Z" },
+ { url = "https://files.pythonhosted.org/packages/01/e1/9c7486d4afe8fdd1fe0ad60139f8aa91427381f409af6a29b609d8fdcb3a/regex-2026.9.29-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9e4482589065c8ecd761cff522dcd85f2d39e62f551e37e025d1c7d54772def3", size = 854792, upload-time = "2026-09-29T00:46:31.358Z" },
+ { url = "https://files.pythonhosted.org/packages/26/c7/49d008ff5f741d9a9799d7315556f3a12b983ff0fcd2cdfb62904bedafbf/regex-2026.9.29-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d60030baaa7bfbb02d650c126cdcddcb6e33dbff14d819434c8fa2fdcaeeeba5", size = 793050, upload-time = "2026-09-29T00:46:32.775Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/a1/46ba549e65562ca04608b24179b8a7bb6f146ae0e7c6d7f5e70f3339c8ba/regex-2026.9.29-cp311-cp311-win32.whl", hash = "sha256:18ae8eed4526e35bdb754d61562b90bf5c00a67fdcf3cc1380dd59597486631b", size = 268940, upload-time = "2026-09-29T00:46:34.179Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/4a/aab232183c70fdcf77bcf0c51819da02ec522e393e6a0bf00bcf2142e21f/regex-2026.9.29-cp311-cp311-win_amd64.whl", hash = "sha256:1043aedf5917caa861bcb25a9c11460049656bdf0017a90a309fa8f255467725", size = 280641, upload-time = "2026-09-29T00:46:35.484Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b1/7c05954af0f51de376df2ba97f7f78a8b79334c7e5b3d2d9f2aead1f4d3d/regex-2026.9.29-cp311-cp311-win_arm64.whl", hash = "sha256:352cf115a810b357caa35193ab656ecf5ef41056855e82f292c99e8514f8d954", size = 279411, upload-time = "2026-09-29T00:46:37.193Z" },
+ { url = "https://files.pythonhosted.org/packages/84/48/3fdcde9a0baa84d7d25571223265d6e434e114763b438601d54a8028bf3e/regex-2026.9.29-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc79d36d0618752265f0d575915bdc5c5130ecb9c9f6b3bcefeae32e4bdfafcf", size = 497903, upload-time = "2026-09-29T00:46:38.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1c/4ee3e97c76f53940488dfe7a7e18705e78daac8cd7fb161d246b9e328449/regex-2026.9.29-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3a21a9509d0ee88e7a70e1ad228cd2f0e0fd1e187458db132e8a8d18c97daf9d", size = 296416, upload-time = "2026-09-29T00:46:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/37/14/f3f0ba083d2094392d5eabf56db5ea6ba469fd6e927afd187042054ea68a/regex-2026.9.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f57dc6b8fef170f105d2cf5cdce254f47b137d7755086cf7050f47e16582abba", size = 293633, upload-time = "2026-09-29T00:46:41.959Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/72/67e7a8ce17f1aea49df215564048efb49cc8c2b31a0e0fc30f36838f8516/regex-2026.9.29-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f93bc1c3486ef3747e07c9d7c1d0a147b8fbaab975f80e348aed6f71309dfaca", size = 805885, upload-time = "2026-09-29T00:46:43.373Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/78/25436bcfd4d2260b4b4090094d55d7ab53ec8a1ab4865a0b8bcb33c7d5c0/regex-2026.9.29-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e1d3a4cb7993b708f0ada8d0c84590efd853f169e7147d2202c9da503180242", size = 878344, upload-time = "2026-09-29T00:46:45.328Z" },
+ { url = "https://files.pythonhosted.org/packages/97/e6/a09ec3a23ae41d6179880e67f0aace9284b2d95f2d7b326eff203f8eec5e/regex-2026.9.29-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dabee8f4935e731fb46b2a3091bdda0d3d94b3bbfb907d2b4f12eefce4009619", size = 919181, upload-time = "2026-09-29T00:46:47.041Z" },
+ { url = "https://files.pythonhosted.org/packages/26/83/d2fbd2e4e3afb1167daa825187d196f313cbaa1a4768f311fb041bb0e3d2/regex-2026.9.29-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39ab5894d971f9ac68baa6eca5c50387db579cfcacf36ae8df3feceb1815e6d0", size = 807783, upload-time = "2026-09-29T00:46:48.894Z" },
+ { url = "https://files.pythonhosted.org/packages/46/0b/eb429a7016610d44fc89a597163f8c9127505f0d7dc724dc9effbb6a3ac0/regex-2026.9.29-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c1a9a6651197fbed6f0212591418b9def774fc3f8324f78d1bf0e6a63e5f8aa1", size = 783465, upload-time = "2026-09-29T00:46:50.64Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/07/58a3c0153c7476898430f6a7cf3d9062a1d17fbea4f43399ecaf411c7b4c/regex-2026.9.29-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87fb80cbe3557e27e7b28b995c2b2eedf689b8886f941ab93e0e288f0976518a", size = 793519, upload-time = "2026-09-29T00:46:52.396Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e8/161b94d39164520e21a7befe0245569bf7fda4c7cf1fc4e2df2b5def49da/regex-2026.9.29-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3c5c2ef13797466aa64170cbb66ad98a32351dd4127694cea7199f80f213750d", size = 869293, upload-time = "2026-09-29T00:46:54.128Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/07/3b02ed829aa2decdc1955d222bd1e2f99d1c8bb4873bbb9a66b2f0a36bff/regex-2026.9.29-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:59b49507f47479e299a9e1bc41b5cb83a7afda0540625f1dbae886615978acbf", size = 770239, upload-time = "2026-09-29T00:46:56.106Z" },
+ { url = "https://files.pythonhosted.org/packages/42/5b/ba61f6fe062eb8562e742367d177bb75370434138ef6c9d2a27114f8d613/regex-2026.9.29-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0dd8af32e9f7b56b7f95cc1fd79b23054c3bdc172392ae560acc24d57b7ffe71", size = 861973, upload-time = "2026-09-29T00:46:57.665Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/27/767259b20e8a842948990f5e99138d6c077248fd42f8b5468b1d9ca4b814/regex-2026.9.29-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db5e82ba15c142425b8406690032df89e39cca4a2e8afbbb9a3d84edc2373ac3", size = 796470, upload-time = "2026-09-29T00:46:59.236Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/05/2566c4ba849b68a8ab81a6bf428fa79d20aae7ddee83979103c0381df254/regex-2026.9.29-cp312-cp312-win32.whl", hash = "sha256:d0c3082bf79bcd6a614d55916590ad4b8f93200e10b97f463ea5d9d07c9b5f23", size = 269327, upload-time = "2026-09-29T00:47:01.135Z" },
+ { url = "https://files.pythonhosted.org/packages/93/19/489bc8db91196381c935752df01ba3f607140daece33b78d88573f028e64/regex-2026.9.29-cp312-cp312-win_amd64.whl", hash = "sha256:fdd88ed5e20b1bcdd234421e454962c971aa44b653bdb7f1ea9ef683e90fb649", size = 280334, upload-time = "2026-09-29T00:47:04.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/47/fb88ba779d0e5e7d4b0ec1aceeb13845948a2cb876bd572a2d1dfdba090b/regex-2026.9.29-cp312-cp312-win_arm64.whl", hash = "sha256:4fe97894d1b306c919b4e50def1e6f6c522f4d03a7283811f4d108f1ce5d3ac2", size = 279614, upload-time = "2026-09-29T00:47:06.541Z" },
+ { url = "https://files.pythonhosted.org/packages/79/d5/6080f7d1a6e7e36aa720f806ac93c035ba39c209ae6cc510e8ef4c0279c6/regex-2026.9.29-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1a0d5117230dd46b399a30a38afa44f79c99f3168988fdc4f425c3f928b39df", size = 497622, upload-time = "2026-09-29T00:47:08.251Z" },
+ { url = "https://files.pythonhosted.org/packages/00/71/c87fc7a2e21a42f9d57489db32951c37eef56d153840459a80d464f0321d/regex-2026.9.29-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0fe9834e5aeccaf19a0d8feb296d66a24be1a7c9922002f842a682cd5abb787", size = 296281, upload-time = "2026-09-29T00:47:09.764Z" },
+ { url = "https://files.pythonhosted.org/packages/11/9e/aa0f4cde3bc4688c1d58b0cd8415edd708339bc0bc401a195b0b1e8c8f0c/regex-2026.9.29-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c90fcf7804ea0a54b896ce0f2b9565350220b8d4890fd0db461a476a4c687963", size = 293472, upload-time = "2026-09-29T00:47:11.723Z" },
+ { url = "https://files.pythonhosted.org/packages/90/d4/e835c487850ed922a8d6074f953b888c8ea99775c76b9ed5f8a4d72eab92/regex-2026.9.29-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e11edba5bc344a32b029a7af9d4b3173982dd79eeafa0b9dbd787364414b0509", size = 805966, upload-time = "2026-09-29T00:47:13.235Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/57/ba8809847fbae8d2cbc71367c6ded510a7ec88bf52493c65efc1acf4effb/regex-2026.9.29-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bb90e7177944b6684738c1fc36aabd2dd00d1de3be7dbe09f91e196f1bc0dc81", size = 878346, upload-time = "2026-09-29T00:47:14.877Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/52/e3da19fc3cc15ef67ab67e121e87887c3bccfdb683a7a9ec557c460ca5b7/regex-2026.9.29-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d06fcdecc10fc7954d7c8f27a03c96055fe525274dc84a7b0dbdc3d6b9e03dab", size = 919250, upload-time = "2026-09-29T00:47:16.622Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/8e/c1ed81f55f992f6aa0b699a592a50c1ce9e6d44ff1aee2c14c0537dcef9c/regex-2026.9.29-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d49c18f1ea294cf4adde2e5ac256e98c82ea9d708462ce4bf799dffa7cfe8a2c", size = 807902, upload-time = "2026-09-29T00:47:18.268Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/bc/5a6886eb470e41040e21e05b75024a18b6ebfe7ea400b72094a60f949101/regex-2026.9.29-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e778bfccd63075167709136afbc251c1f683758d5bf49c803c60ac3f894ce6b", size = 783514, upload-time = "2026-09-29T00:47:19.921Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/52/6d951d453b023c6edb880f1ba474291b53b8ce1cc438b96a9db6d791d991/regex-2026.9.29-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:686ac5350fceae63830bb98805fcb8039325bf4c06d9f6f048ff65229d5bffa5", size = 793466, upload-time = "2026-09-29T00:47:21.552Z" },
+ { url = "https://files.pythonhosted.org/packages/99/b9/d5a41adc08360f5eee0dc4846c578f002366947211fc8af5a69a64ee7b9f/regex-2026.9.29-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:26ec4ccce55aa533fbd603d08911b01101a8fcfec987845ac3ae2c7087b2bde3", size = 869464, upload-time = "2026-09-29T00:47:23.276Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/32/d76c9d91f5d798e2e9e67f6f85ec4ae35445ac425f7454797311cecb80ca/regex-2026.9.29-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a655d34b2a6943af32401f3d94f72e9d731f6ad16285815550bf2b4ee69d420a", size = 770278, upload-time = "2026-09-29T00:47:25.193Z" },
+ { url = "https://files.pythonhosted.org/packages/24/00/aeebdb540c620a0f7317f6d6fad80a47729ecf0599a24b5c34ec155351f5/regex-2026.9.29-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0c992c19cd45058a4b92f68f139c93db168b48fb1f322c9a7cd620806afb6b51", size = 861949, upload-time = "2026-09-29T00:47:27.005Z" },
+ { url = "https://files.pythonhosted.org/packages/12/62/d0314bcedfd3586197e4596931fa220260eb2385bf53184e5b9ae67db24b/regex-2026.9.29-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb8912f565b8cdbbf27debfe00df04202c20e2f651b9e32767930c5eace3621", size = 796602, upload-time = "2026-09-29T00:47:29.233Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/c7/d5a8c13a613facb03e0fb55c1ebaaf7bb35d8e2c1abe8bef8dca809fc1d9/regex-2026.9.29-cp313-cp313-win32.whl", hash = "sha256:4d7d93613b01b0199961330e49cfc52d479b3d5776c56c691db31130c0a07d91", size = 269306, upload-time = "2026-09-29T00:47:31.14Z" },
+ { url = "https://files.pythonhosted.org/packages/80/a7/bf93a3a6afa5f7bc16b7afb94ae581b01cae620b8ad56bd8f9572a985959/regex-2026.9.29-cp313-cp313-win_amd64.whl", hash = "sha256:61956f074ecd123f55adca68ee3eab46e6a07ad3f8e64e6db95dfacb444f55c4", size = 280307, upload-time = "2026-09-29T00:47:32.709Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7d/388274e53605a86297f433a08102a7bbdcf9379d47683d307ccaefd88e2c/regex-2026.9.29-cp313-cp313-win_arm64.whl", hash = "sha256:bfc71e6d970419c1309b3640305298643e2a734cad3f7cfb6d2ddee4175ab53d", size = 279599, upload-time = "2026-09-29T00:47:34.674Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/d9dc6f02f569625faf67a4daec926cd5023472dcd69bb44286dccd5a5ab3/regex-2026.9.29-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:957bb708e8057ab1649ba566456429d691ec9b90d1c9ad1af1ba7ffbbeaf05f2", size = 497598, upload-time = "2026-09-29T00:47:36.541Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/83/9b693a3fd1451381e812031a8961ec5b3b8f0c8cc6871f14c5223642804d/regex-2026.9.29-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c9b602fae1e00b7c035d661ce85575365719192a7b46784bd71cf64c68053aa0", size = 296250, upload-time = "2026-09-29T00:47:38.233Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/5f/52bc2abc3fef040cd9de76ab29c918d6a717a454ae2b9dd7938b0c95656d/regex-2026.9.29-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0166844493626c5015c6088ee15c9ca2fd060ca15b7641d1657da6a58432ae33", size = 293518, upload-time = "2026-09-29T00:47:39.957Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/fc/cf50671215ee0057046980b4571ef8646a005819bb67f0957e779ed107a5/regex-2026.9.29-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b97a38fb4c732b6832db6bf108963adbcd82ef1268ba2025dce390f45af75efa", size = 806037, upload-time = "2026-09-29T00:47:41.676Z" },
+ { url = "https://files.pythonhosted.org/packages/14/4b/dddef8fc15c63e4347cc9efb138d0cd306f30e6c98acbcc81a8f780083b9/regex-2026.9.29-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a540abfab208e1b7ef2df231c40ef3b6cbb30a0aad6204e9b6a81c10a6794628", size = 878881, upload-time = "2026-09-29T00:47:43.755Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/cb/38daabed32d28f7e58a06e9344ce00dc67952e9996bc578ed6a29fe1240e/regex-2026.9.29-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ddfa987262763c3c22a8367d2a49c244b018a74c3a8e3ab1a864119ad45c5633", size = 918684, upload-time = "2026-09-29T00:47:45.594Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/4d/041d9458a645fee4fce4d642a89d27271a3cfcd91095104f6dde44da70bf/regex-2026.9.29-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f7f7aa47b229f2b39a2ae2596d2ad5625d77b5eb9856fac2dab3eb506cdd0a0", size = 807176, upload-time = "2026-09-29T00:47:47.372Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/c4/4383eed7aa5aef67616cb1b3f3ad06b7c624c4e6cced48630cd5ce133d85/regex-2026.9.29-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d9b77b25b4f395f92de6099ab08e8ae2bc7e51dfe157f22900902243a5cc90c7", size = 784315, upload-time = "2026-09-29T00:47:49.518Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/a6/0086ad31cebb183c637d3198547075aa493afde308e1ff61fccccb29ba6e/regex-2026.9.29-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:34b6925af9853bf461950e6508910f179fd6e9b1a7ec8548e069606b7e51a26b", size = 793748, upload-time = "2026-09-29T00:47:51.279Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/a0/f9005cba3f629a859573fc5d1224ea4e1f97919ec8581d018e03a351a604/regex-2026.9.29-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:addd736a0547d553283adaf4e05d7104e7f2c7b0b092e9b4d28756825f14531f", size = 870302, upload-time = "2026-09-29T00:47:53.368Z" },
+ { url = "https://files.pythonhosted.org/packages/01/4f/e1a3e46bb5315a4e18b01a990e7a28e2a16595609d50c442baf2815a3c65/regex-2026.9.29-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fe3fa1dd453ed5c7f5ea23a26218329790ed7197a99b90e94330e313959a7f52", size = 770299, upload-time = "2026-09-29T00:47:55.606Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/fe/f303b4acfda44e1ff1379368748c1ef2dad04a6a8e9c0ecbc970b19d97ca/regex-2026.9.29-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0cc63b5e47c12a48d90c7e9d7de6a035dd14f62868aaedbb4e0ff8ba2b8bfe7b", size = 861570, upload-time = "2026-09-29T00:47:57.617Z" },
+ { url = "https://files.pythonhosted.org/packages/60/b6/b4f7e99249f596017c60ccad5faf9310fc8e3e59bb2244940a90a1b0bdff/regex-2026.9.29-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:724184b4aafed865e4f13ca313fdcb43024300c028ec67319cfa16847d84685e", size = 795967, upload-time = "2026-09-29T00:47:59.922Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d3/fc865a4638d9f6762192b6bab5b7aa1f33a90e9e99578c2e111e2a63c8c3/regex-2026.9.29-cp314-cp314-win32.whl", hash = "sha256:c6c8fabf1dafc1f1ddcbb67896d3f93efb092e8c4b6322d7389b944e76a484e5", size = 274758, upload-time = "2026-09-29T00:48:01.8Z" },
+ { url = "https://files.pythonhosted.org/packages/31/e2/c2b466924ccbeb874862968ca638051b15a8fd29d994a0e99004a5cbf78e/regex-2026.9.29-cp314-cp314-win_amd64.whl", hash = "sha256:1c2a0026062abcc321a53db4a185ceba0b59a66b5d37b0808917a88b55a5257f", size = 283817, upload-time = "2026-09-29T00:48:03.614Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/42/ea0f8dbaa924fa75c6338935eaee2f44dab369b27f02db1e03d74344b049/regex-2026.9.29-cp314-cp314-win_arm64.whl", hash = "sha256:121a76a0985db80ceae9e171c337f8c927868e37d01b54e3ce87bc87f9c6a208", size = 283663, upload-time = "2026-09-29T00:48:05.624Z" },
+ { url = "https://files.pythonhosted.org/packages/44/48/d58e5081119f5c223bbb37d2340acde3d069e1df8e8cd166c37502eee4da/regex-2026.9.29-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e31f72490b7c12f7790e1e25c3afffd20503ee1bfb43461d7838b871ff244b19", size = 501393, upload-time = "2026-09-29T00:48:07.833Z" },
+ { url = "https://files.pythonhosted.org/packages/72/3c/c49945287d4f9efee7d41f98072f8ad880efb8f430595a612fbdea996a4e/regex-2026.9.29-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:80ea96f5c1a30bf09007d48466521d9c294bebe197c708c3359096e3e3691632", size = 298237, upload-time = "2026-09-29T00:48:09.684Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/1f/688cb61c3d4cf7bcc1ed444b5cc49399eba3e51c469ae285cf87fea3022e/regex-2026.9.29-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:554bffadcbcb6d5f4e5fb10a61cc52084b9a63d1dab5f10bcd2c4343972e8e2c", size = 295936, upload-time = "2026-09-29T00:48:11.454Z" },
+ { url = "https://files.pythonhosted.org/packages/26/a3/de43ac6b877b7d09c19a3a426b1bd5acdd209eaaf68f406466f80439ccf6/regex-2026.9.29-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:864e9b87ac33c3fb9fb4ad48166d4fdb579c351d5c77deb0d34bccb36a775cd9", size = 816905, upload-time = "2026-09-29T00:48:13.321Z" },
+ { url = "https://files.pythonhosted.org/packages/62/14/9940763201c51d537786304984c67d0fc3d2ed18837ffb6f09a869f6b6c9/regex-2026.9.29-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:044265d77d94f5e3cb2fd72c76723807c429cb8c533e9d4672d0334a6f14f588", size = 881527, upload-time = "2026-09-29T00:48:15.313Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/e1/c842d8df0b23245ebf202f8ab9c39fd48e2db39959454ec39a41c8c72082/regex-2026.9.29-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2089fe39c406784d90101c726755ffa1497bb74638fd434300d2b88006186de8", size = 923115, upload-time = "2026-09-29T00:48:17.328Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/c1/98622479e3c354a446a75232e522d747d2b3df23092dcd8a5309380a2020/regex-2026.9.29-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0def9fb6abac55492d6d51cddb7225d07d6f279e774e0adc08569a54a5fc8d46", size = 820674, upload-time = "2026-09-29T00:48:19.32Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/d0/5808c95f9c79ed27b5eedaafc3df6239ec56a49f2e23ea8f831b18427c82/regex-2026.9.29-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:888d60953908dcf761aa320c3e390ab8556efbdb551ace63921de90f6ae0848d", size = 793306, upload-time = "2026-09-29T00:48:21.615Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d3/021ca2638671ad20603bcd9b4d5bfa35d2610cd216a043ea7f0b44ea39f6/regex-2026.9.29-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ed511a0708e2297e1d6431e7fb217e3402791e491e02da800658ace4973df1bb", size = 803103, upload-time = "2026-09-29T00:48:23.871Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/2d/755c6d13ef9c657378013676c391c7a402166b3f419a464a3e058dcbe533/regex-2026.9.29-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e1172147d28d8fbcf8cb8d26c41506169f5ad8fe9ec969cb116835a19d4d8eca", size = 872175, upload-time = "2026-09-29T00:48:26.255Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/fc/e1cab183b9dafe8597f58c1c766da9bf96204d3b2f232bcf3eeb75ff7b6c/regex-2026.9.29-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:92f05c9c42bde5785dc48770bc2194d9f7442544156f951e19cd31b096cec562", size = 777362, upload-time = "2026-09-29T00:48:28.389Z" },
+ { url = "https://files.pythonhosted.org/packages/06/7c/e10ea17fba31fb4a1f9d13ed53a2d2a9066a2aea58d7557e263f6d99e7b0/regex-2026.9.29-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f37964e4a5e993d2fd45147741e9dff7f34a2d8c00ab94c4ea0514a4677f959e", size = 865606, upload-time = "2026-09-29T00:48:30.4Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/6e/69824d9aee1fd41c54ea7264654a47c8d9d84d8a228e11c2bcf4c201ed81/regex-2026.9.29-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:951733b1bbdb71e377cec567b409f1a7881b47cfcad84121aa74cb575fa425ea", size = 807945, upload-time = "2026-09-29T00:48:32.375Z" },
+ { url = "https://files.pythonhosted.org/packages/89/22/857050a86e21ce60193e02a8ef662521f2e263a645c8b1b905fc136b61a7/regex-2026.9.29-cp314-cp314t-win32.whl", hash = "sha256:65b408d8fcb273e3499e7ef2ce796810da1becd208c7fb4373692a242d79d461", size = 276758, upload-time = "2026-09-29T00:48:34.72Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/96/56808fe029553d7d4c703414f2a527faad2ea2bfa9ca094a2e7f8762b530/regex-2026.9.29-cp314-cp314t-win_amd64.whl", hash = "sha256:bf48516e35cf848390ea68850aba53e7c333720d2945b4d2c25b69fc5171723f", size = 286527, upload-time = "2026-09-29T00:48:36.864Z" },
+ { url = "https://files.pythonhosted.org/packages/01/aa/074e2cfb3d8101a6a764aba5f7c5d1e21de087483e35bdc0c4ce2eb60364/regex-2026.9.29-cp314-cp314t-win_arm64.whl", hash = "sha256:9173db3be74a35cb6731701094b98120f7ee4876a287882a59cdea1fa7da342f", size = 285954, upload-time = "2026-09-29T00:48:38.901Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/dc/d84990386c9dfdf8c377f00f371b241fdc9a2c8aea0e3d66941b2e51be0b/regex-2026.9.29-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:c3589f40749acce747510bf5d589d54e376cb0930ea58b35effac97e5312b0c1", size = 497836, upload-time = "2026-09-29T00:48:40.858Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ab/a569ebde875fa12ff8c6c9a30e07503620f195e4be4d54c3d3ee8eecc283/regex-2026.9.29-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:32ab11df9677ca80bcbb5fe4eb1da9109a5019239a054836efc6fa1c64e683cf", size = 296244, upload-time = "2026-09-29T00:48:42.952Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/3e/7d548e82a108e7c8b2d5246650e397a2f8db599f9b2e975466939c5b4e70/regex-2026.9.29-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:7c03031610e3e6ed1768a2b7a8fc84637c1257b50c5eacaf094c6e17a84fc563", size = 293748, upload-time = "2026-09-29T00:48:44.985Z" },
+ { url = "https://files.pythonhosted.org/packages/40/34/a8e19a52f452bbb07b32a2bef70dcdf90c2737049749f74cc12d7486fb4f/regex-2026.9.29-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:42e82e578c904445d4c8a35b8f28052cf567593215fa5db06266fbc6f77aaa2e", size = 807840, upload-time = "2026-09-29T00:48:46.948Z" },
+ { url = "https://files.pythonhosted.org/packages/88/7b/11fbd4640b3bb82b72822a63c20ade4013d562d291703a9debeedc24e682/regex-2026.9.29-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0b65c72739f981377c9c22e0c5c3cd7f42da7bd8a3c9209330fac772c7d893ed", size = 879330, upload-time = "2026-09-29T00:48:49.168Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/55/de58c74f1f4e31586d83eb39c56872d686c4e0d0966d151884c833b94ced/regex-2026.9.29-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4408b2b27a95ca8cc48b7411945753773353b5c93b307754781086c99d3a576f", size = 919251, upload-time = "2026-09-29T00:48:51.322Z" },
+ { url = "https://files.pythonhosted.org/packages/81/42/a8c480f6dd5ac59fa28ddae79afd9d7ac7e596fdb61813adc65bb6e674b8/regex-2026.9.29-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a714befaacbd10092ffe4cea0d3c5f008fb9efe9bc322c715bcdfdee414b9a3d", size = 808808, upload-time = "2026-09-29T00:48:53.529Z" },
+ { url = "https://files.pythonhosted.org/packages/68/60/0bc0d1ec8b37ad64be6fa30e035251f11de9667a0fac9e82ee74517d81be/regex-2026.9.29-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33026515aebc0e70d1c89978e53e8d695d35d9e472f8d5b34465ba3c74028650", size = 789907, upload-time = "2026-09-29T00:48:56.036Z" },
+ { url = "https://files.pythonhosted.org/packages/da/84/116a3ef19b3acfe81077f0bf2cbc7714a5e94bc8935b7243ab61cb0f1c3c/regex-2026.9.29-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:31b003f9a070335e2a8233ee9b14a3ca8e6d792012ae011f741bf0aaf11744c5", size = 795770, upload-time = "2026-09-29T00:48:58.284Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ba/e38c3f203e7e7e18c957d48e6cb6dbf96c11e95a44efa4a480522afc5d6d/regex-2026.9.29-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:c03c6eb6ece86dfdcbb34799efaa339b093132e1aceed491ba5e08fe06cdf699", size = 870671, upload-time = "2026-09-29T00:49:00.506Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/0f/9ee0b0cb76c55f63684bd7fff554978e8773b4fc86e2bcb2d50772dc1086/regex-2026.9.29-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:a5300757f8a68f5b6cc33f57338d72a0e3589c5cc9ad5f8504ea06f028be582a", size = 778631, upload-time = "2026-09-29T00:49:02.984Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/19/e6e3eeb226af5872c4958002f6edef4e4f40ea4cc5f5665023f2019eb045/regex-2026.9.29-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:80c7cadd3fd2bfde5df8aa0787e315812cad0c313a753095d02f4c2b6c01677b", size = 862187, upload-time = "2026-09-29T00:49:05.264Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/62/823c102e106bb2711d6b7dfe5981552fe4467b2969c46a20c5c383cf498c/regex-2026.9.29-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3f1e6cb402a89457582cd696f982559217d13484a193202c394015297968c86d", size = 798423, upload-time = "2026-09-29T00:49:07.644Z" },
+ { url = "https://files.pythonhosted.org/packages/37/e0/e927776258fa70b2f6feffc3be584ffc85ba4c1e20a320f0aee9a632fc7d/regex-2026.9.29-cp315-cp315-win32.whl", hash = "sha256:a64b85a4760337cfefdb27d42da6ed8b58e8cde3f2d57b6ef43e76ef6ea9ef47", size = 274757, upload-time = "2026-09-29T00:49:10.513Z" },
+ { url = "https://files.pythonhosted.org/packages/77/04/358de85d1860238e1b4fa98fc2c80c990124a25d2e14739e28cc02c25562/regex-2026.9.29-cp315-cp315-win_amd64.whl", hash = "sha256:b3e445b66c80b4eb4234e855ce94d9adc183eedbd632816228d89930b91b2c5b", size = 283824, upload-time = "2026-09-29T00:49:12.849Z" },
+ { url = "https://files.pythonhosted.org/packages/92/d3/d5c5b264784a5ab2b0f8cf620c1eeb4dbf3440d306761905e7d99345bef5/regex-2026.9.29-cp315-cp315-win_arm64.whl", hash = "sha256:8f39588af4731c8923c26810eb3b33f76f17633985e40f59c3cd45a33805a895", size = 283664, upload-time = "2026-09-29T00:49:15.331Z" },
+ { url = "https://files.pythonhosted.org/packages/02/dc/f63ec2c201445ce1150fe780f5c56f16a10124d9a9da3a93161dbb0d8892/regex-2026.9.29-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:fb99cc9d45f48895d9d67f6a0b8a57f08d39c174d9f25ad97a313e0470267b1c", size = 501549, upload-time = "2026-09-29T00:49:17.705Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/5c/d2a698dc6bfc11fbce03f1cb0249c13284e93b79ed11f893edf6fac431c9/regex-2026.9.29-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:720537c7ea6f80dc61913184edb0ce2497a306b39ef19f28505b322553d52bdb", size = 298187, upload-time = "2026-09-29T00:49:20.171Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b7/88dcdb38cd3935d4ee9e9ce9b8e56cb3b3518d1f020acfa7dd62ad289bf8/regex-2026.9.29-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0fd2c901cc307a745ad4bc87f20060d7a0825a3371d1e93488af22e7a387f78f", size = 296157, upload-time = "2026-09-29T00:49:22.342Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8e/ba6c01dde33a69fc294b38b43f6677baaa5735a6248f39708031a738158a/regex-2026.9.29-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b11b589e00095ec69cf79841a76360f9b079e95b0368a25b5ebb951ab0c157ff", size = 818468, upload-time = "2026-09-29T00:49:24.612Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/f1/2586693e3a2d6b1247852593d37a6c17b42a92ee44f7cdcb9a0c1494e64a/regex-2026.9.29-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d7cab119d0df0b9413f106b4d7fc34f2872d3574ed3806fb48959c830b1537da", size = 882825, upload-time = "2026-09-29T00:49:26.996Z" },
+ { url = "https://files.pythonhosted.org/packages/30/51/084f3e7bdcd0e9c33665c938cf5d134dc3548cbb4a75f0197ec7bfd754b1/regex-2026.9.29-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b89efc38431793d28b7cd91227e2f952ad7c48df19132b17f43a5fec3c14143b", size = 923314, upload-time = "2026-09-29T00:49:29.822Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f1/066c6fc23b7dc229789c21c880b5ba5ad689fb95fed12e078266f55a1f9b/regex-2026.9.29-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80a5ea3b4fd9d6a5b9a44f7976a9acaaab35aa3c1f6b29e5bd857dfabaded223", size = 822222, upload-time = "2026-09-29T00:49:32.404Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/56/592cd46fdb8f2f8682a1d7fd1310e4d0bcb93fbd0e6bbe4141ac28240227/regex-2026.9.29-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19959129885356df0e97556856f77eb2888380dac18bed075a7c05c5128c618d", size = 794882, upload-time = "2026-09-29T00:49:35.076Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/4d/d65384bb071c864b01aa8314e3a6a687845ebd57588390976edc960c218b/regex-2026.9.29-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:6a1a824fbed817e0a891103886b68f063b1e83cc51bc97192a90a60195a9291f", size = 805887, upload-time = "2026-09-29T00:49:37.395Z" },
+ { url = "https://files.pythonhosted.org/packages/65/b6/358de0d8f40d5178e4f7e7e121cfd5b961c812b77a055d11f5079e3f8fd7/regex-2026.9.29-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1ba8c6a416569ce0d37e83e28a254a61dc99a419084dfb6476cea02d997f74fa", size = 872901, upload-time = "2026-09-29T00:49:39.927Z" },
+ { url = "https://files.pythonhosted.org/packages/00/06/6bfded72d043240c6b52bbb5e16f639d81affbf7484b4fe2ec45f3d4afc9/regex-2026.9.29-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:446654b29bfaa30500d80947eda42cef1449dc8a87f4e3cf061cc8485d3a1f0b", size = 782970, upload-time = "2026-09-29T00:49:42.581Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/20/9f418a50baa78b3ed8308fcb0cc49e472dd000b7ef935a7295af202ea744/regex-2026.9.29-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:bf3c49863c23a1ad6da9c30351aed6cff8d5ddbeb63c5c8420ae54e98c7d0138", size = 865441, upload-time = "2026-09-29T00:49:45.238Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/29/817c7eacdeaf8463123e949bd394c39ad024eea1ec38ddf5ad141da2f3bd/regex-2026.9.29-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:01000ddf0e3ffef97f2413ceb514f6313040106b6d18a03ee00a4fe35c1eb1db", size = 809431, upload-time = "2026-09-29T00:49:47.878Z" },
+ { url = "https://files.pythonhosted.org/packages/63/0b/83aab3b5b739947f744135a7a3a446e25433ebc92b05e01aae197ccbfdda/regex-2026.9.29-cp315-cp315t-win32.whl", hash = "sha256:c4e38dd8f39c43a91d2410ad2b85610701b0979342c3df1d69eaf8e838c757d8", size = 276964, upload-time = "2026-09-29T00:49:50.524Z" },
+ { url = "https://files.pythonhosted.org/packages/72/f2/6314b5fc68789b5dcc38885bc6e3d6986b34fb3372b7231088ee5cecaa05/regex-2026.9.29-cp315-cp315t-win_amd64.whl", hash = "sha256:e2c89e9b762c57f59d5e99ee8b20202adb892e35f8d3485741340999ca55058e", size = 286487, upload-time = "2026-09-29T00:49:53.224Z" },
+ { url = "https://files.pythonhosted.org/packages/56/bc/97b2245c8c7b2dd01f2db74f2bea003cd33c15009b4996a2447f46b5325c/regex-2026.9.29-cp315-cp315t-win_arm64.whl", hash = "sha256:e8c65ef3862a8ad6e86492b6ed9327805dd66904c012bd3649dc67d822ed6c34", size = 285955, upload-time = "2026-09-29T00:49:55.655Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.34.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
+]
+
+[[package]]
+name = "rich"
+version = "15.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
+]
+
+[[package]]
+name = "rpds-py"
+version = "2026.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/42/68/3bd46b8a5e01d3c2ebdf9c5e9497912e3fe0cde02bac21a7130ca866e403/rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12", size = 63948, upload-time = "2026-10-04T16:32:36.469Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f5/6d/ddb8dfcf41892ba9c672d27988592c038d30db61708230313831b6780a1b/rpds_py-2026.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2", size = 344947, upload-time = "2026-10-04T16:28:55.207Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/b2/632c3d034961149eff606e3e357547620a9ab37b849ae5815f67838753fd/rpds_py-2026.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044", size = 346770, upload-time = "2026-10-04T16:28:56.884Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/45/4df346410bb873d03ec60c3b891d1fceb8cabde1971d29844bbb625cc296/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa", size = 377795, upload-time = "2026-10-04T16:28:58.193Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/b8/a70f302207e4f29f873c01a986fcac5def49b9c8224328cef1ad7da6b7a9/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52", size = 383137, upload-time = "2026-10-04T16:28:59.411Z" },
+ { url = "https://files.pythonhosted.org/packages/13/e3/3aa28a48a01169d0e20728bf0e5ebbea3ae2774285b40853ae49b3ef332a/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f", size = 496789, upload-time = "2026-10-04T16:29:01.011Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/a7/414e8d088602d66ad6f47c65bf07fe5090992a8574cdd4571c1a83b647ed/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7", size = 395929, upload-time = "2026-10-04T16:29:02.465Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/5c/aac3b960fd55d37b49f77c0eebbe043eaf36762ebd48e7b46951e59c1c05/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff", size = 375426, upload-time = "2026-10-04T16:29:03.944Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/80/77987a41c40be7202a78d9aed4a650bfb05792237cfd3b9e22638d990fe5/rpds_py-2026.9.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e", size = 381497, upload-time = "2026-10-04T16:29:05.308Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/d0/640b02eb1ecd71a913c1178b6235287b31006266bca9a26862e80e497b54/rpds_py-2026.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888", size = 405740, upload-time = "2026-10-04T16:29:06.578Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/8c/d034195d45d215625a23f2ae85dbbdb86a49a93a51272061c97c80b9d316/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834", size = 555850, upload-time = "2026-10-04T16:29:08.115Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f8/532f1addfc86207bc3cf23749638bb0d457bce891595012ed8a08d0543b8/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391", size = 618661, upload-time = "2026-10-04T16:29:09.554Z" },
+ { url = "https://files.pythonhosted.org/packages/50/5b/b0f2c9833a36a5b1f6ac5cb0e635fabacf713266f46ceefba2102da0bf51/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a", size = 583530, upload-time = "2026-10-04T16:29:11.072Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/20/464ed4b98d3c8c6a334f3b5e24d3886de04b05658a95fc40a37e0e6e4775/rpds_py-2026.9.1-cp311-cp311-win32.whl", hash = "sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1", size = 208125, upload-time = "2026-10-04T16:29:12.408Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c9/384a9b62f8cd89c8e3e609b8c9052c140e8c01d807261e0517c904b8bf29/rpds_py-2026.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f", size = 226045, upload-time = "2026-10-04T16:29:13.751Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/3b/0a691f0dcb2301fa9c736346da2d0d4991778373283301c5c12b8f952f3d/rpds_py-2026.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12", size = 222636, upload-time = "2026-10-04T16:29:14.989Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/34/a828586ea3329fbb50895b50e9cf98ca3d924a9f41a0b26d443bff1b3794/rpds_py-2026.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af", size = 346822, upload-time = "2026-10-04T16:29:16.258Z" },
+ { url = "https://files.pythonhosted.org/packages/90/81/ac6a0d064982251856ce009c9e1dd51a34110b3c055aa7d1aad18b4899a3/rpds_py-2026.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b", size = 341380, upload-time = "2026-10-04T16:29:17.706Z" },
+ { url = "https://files.pythonhosted.org/packages/42/ff/bf7d54f362748fd6a49277b9d6531c394074110fedf50ad791ec59133fbb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a", size = 373053, upload-time = "2026-10-04T16:29:19.063Z" },
+ { url = "https://files.pythonhosted.org/packages/60/de/74b0ccdbbd28687b9b5fdb34c1cabed88352182facaced9d6f5486b8b9ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07", size = 377742, upload-time = "2026-10-04T16:29:20.543Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/6d/b979775a3057b2a5c26d75ecbff60a20a24ef081db6dd76836fca3f20247/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69", size = 487289, upload-time = "2026-10-04T16:29:21.958Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/5d/7c34734ce3ece943d9d6ee0122a74a21bbc75b47acb5b7053c83f6efb1ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d", size = 390710, upload-time = "2026-10-04T16:29:23.378Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/6d/b26eb1e75395925b3a142ed351cbed2ec8212f5c9d937aee3df32c701baa/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab", size = 371261, upload-time = "2026-10-04T16:29:25.1Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/98/b2fdfe10301a9e27af21e634337fbba7baac0bc17fe44619a17c26bba2cb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e", size = 376075, upload-time = "2026-10-04T16:29:26.748Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/b1/c4b8d954e49c3c69cf8063c0c9e0919c99f3d2cecc46311606a1cbe835dc/rpds_py-2026.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237", size = 399358, upload-time = "2026-10-04T16:29:28.282Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/59/9559a7293c97dff0cbb293efffbd36644103883ecc27741af8ef90c84ca8/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3", size = 550267, upload-time = "2026-10-04T16:29:29.851Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/9d/6dc60e49511de4c8b00e74f9c1d43aed4d27f712cdb1ade92f2c199cbf64/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc", size = 614018, upload-time = "2026-10-04T16:29:31.54Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/bc/92a4ecf27301b886232a413360f6b348a81d55ffef654f2ebda5970ebc3d/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6", size = 578509, upload-time = "2026-10-04T16:29:33.01Z" },
+ { url = "https://files.pythonhosted.org/packages/08/53/efb97f2e6589b7ab8394385a73870e0b2ab2a195281c117b21fd6d7d4855/rpds_py-2026.9.1-cp312-cp312-win32.whl", hash = "sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3", size = 205401, upload-time = "2026-10-04T16:29:34.451Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/84/1700cc748d0eaa747486e4e82882d2575224e6e9a3148df583058850c629/rpds_py-2026.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f", size = 222923, upload-time = "2026-10-04T16:29:35.709Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/89/0302215373c2f8b4408b0fdfee955368cc378a98ff59508d47a7e0dc2dbf/rpds_py-2026.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02", size = 218415, upload-time = "2026-10-04T16:29:37.263Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ea/ee88fd9e756ff93fb6b1182a47ec09504a242620e33ce1d20679efefe841/rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9", size = 346229, upload-time = "2026-10-04T16:29:38.82Z" },
+ { url = "https://files.pythonhosted.org/packages/57/71/a097d6552f837500fc36e6b23d09cfb9890c3cc47531f9ca64e149799615/rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858", size = 340514, upload-time = "2026-10-04T16:29:40.405Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/b7/497e85768bf4e0d8ddbaa096a4cac31d1509251dee2728a8490aa367e0b5/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063", size = 372611, upload-time = "2026-10-04T16:29:41.778Z" },
+ { url = "https://files.pythonhosted.org/packages/52/4b/74ab4108916250b6e198e0d3af05bc6835eb046315f22f7a0ceb49667c5a/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025", size = 377712, upload-time = "2026-10-04T16:29:43.242Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/8e/067e77d9d7b3cc793c9d909b7e97e7aadbbb1fb094093b6876902cc96d38/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5", size = 488106, upload-time = "2026-10-04T16:29:44.692Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/b4/c5aae6c2dde269bf955f6b7d35065c655a57e47750d9668052ad67e74dda/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0", size = 390795, upload-time = "2026-10-04T16:29:46.129Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/36/76fab39973ee11e7f9f357c55138197bb01c86f6502cb76487e3b4f42db0/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b", size = 370794, upload-time = "2026-10-04T16:29:47.603Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/fe/cd2a80e6d7b871937a60e935c5d507aa390d143f4ff3636f640b9733d5df/rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33", size = 375673, upload-time = "2026-10-04T16:29:49.223Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/18/7464a9953724e55a3b3206062fa0ffdeaa519584c6aabf65d3956d94f131/rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83", size = 398758, upload-time = "2026-10-04T16:29:50.601Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/86/1534b436700fd49ff411063b7c4d7e938adfabf90895b6cf1622d5a7d1f6/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c", size = 550370, upload-time = "2026-10-04T16:29:52.002Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1c/e1fa82a8a01e3c5820f3ba98a8b2673f642128eb368fa88871b01dd2c909/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36", size = 613106, upload-time = "2026-10-04T16:29:53.655Z" },
+ { url = "https://files.pythonhosted.org/packages/29/55/b20b8c4c3dde8755bfcd5b08492a02d0cd2e26929aedf6199ca2a377d42a/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d", size = 578235, upload-time = "2026-10-04T16:29:55.157Z" },
+ { url = "https://files.pythonhosted.org/packages/55/42/df3f7bbc3f7ab37a8a9db8d6c2ff2c985422899f1f7926afbf7ec3c0b8b4/rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c", size = 205293, upload-time = "2026-10-04T16:29:56.669Z" },
+ { url = "https://files.pythonhosted.org/packages/31/9c/ba5a9569d719bfdd6ce863df4133ac6a1658cf1b07cc3534c31db729fbbc/rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9", size = 222409, upload-time = "2026-10-04T16:29:58.049Z" },
+ { url = "https://files.pythonhosted.org/packages/35/72/f28ca566f6c23c35bbf7445f65eb0364577b25a026305995e24f78b83d94/rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f", size = 217681, upload-time = "2026-10-04T16:29:59.449Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/f2/67b94be1532767803415c1c5a1fd88ea487643d74a673cec1ba140af77bb/rpds_py-2026.9.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b", size = 347178, upload-time = "2026-10-04T16:30:00.865Z" },
+ { url = "https://files.pythonhosted.org/packages/04/37/b751de2b59b0197a1d92a5dd491de88e8a5e928c2e6562581974f1e85263/rpds_py-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35", size = 341878, upload-time = "2026-10-04T16:30:02.564Z" },
+ { url = "https://files.pythonhosted.org/packages/72/e2/5873bc4643c250db9e05d48dc0c93763d4aa68bc3b81164cb1af3b45b284/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0", size = 373963, upload-time = "2026-10-04T16:30:04.026Z" },
+ { url = "https://files.pythonhosted.org/packages/51/03/5acf7632158247f3f6386ff0af3a1ee48167d575037e8d0920594b76d92b/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477", size = 377975, upload-time = "2026-10-04T16:30:05.555Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/00/63fda451b8bffa5808fc8bb311ee7c073b340974b09f273c7b2a145d3d62/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4", size = 490347, upload-time = "2026-10-04T16:30:07.156Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/ae/c093ffd070ba0fb02f76c565d06fecc65ad6e4afdbae78f7031076d3cdac/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef", size = 392962, upload-time = "2026-10-04T16:30:08.77Z" },
+ { url = "https://files.pythonhosted.org/packages/22/9d/d08a1128ab199b2f0cf25bfeb0639bd05119fff4b7c47bec24ef9a8ec23f/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd", size = 371623, upload-time = "2026-10-04T16:30:10.501Z" },
+ { url = "https://files.pythonhosted.org/packages/53/c7/4758ddcbb75609414bbccfcb11d612436f9b3ee821bd2f33f0f1604ee648/rpds_py-2026.9.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35", size = 376997, upload-time = "2026-10-04T16:30:12.124Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e4/947bd7f608ff60faf46dc9d389c3dffd0e3d767d78a0be19978448ef0ce7/rpds_py-2026.9.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86", size = 400169, upload-time = "2026-10-04T16:30:13.804Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/35/fe93e020a0543b5670472c18d7e6af3197c08da571240ce1965c84f85c0f/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e", size = 551615, upload-time = "2026-10-04T16:30:15.332Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/4f/5d2a0136bb03b2a56a39dc6ff92d58a6e3e53a2e17079238b86228882f16/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785", size = 614655, upload-time = "2026-10-04T16:30:16.92Z" },
+ { url = "https://files.pythonhosted.org/packages/09/1c/3f1025aaf70d9bf7272cc41f8b64ee76b48bf01726248138430e16f23b38/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118", size = 579602, upload-time = "2026-10-04T16:30:18.555Z" },
+ { url = "https://files.pythonhosted.org/packages/53/0d/5c72e6204f76610608706da32b6b7e11ef7e10317558a7bb15a122e008dc/rpds_py-2026.9.1-cp314-cp314-win32.whl", hash = "sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc", size = 206315, upload-time = "2026-10-04T16:30:20.05Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/0b/489d48abbcc7d70cf3fbf662d9d22abf1f4650761c0a9ae05260800800d3/rpds_py-2026.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c", size = 222852, upload-time = "2026-10-04T16:30:21.604Z" },
+ { url = "https://files.pythonhosted.org/packages/91/16/bbb05a7e6a10cf79ba639be7f799d770ee15f64175cc61d081b218dd402a/rpds_py-2026.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31", size = 218709, upload-time = "2026-10-04T16:30:23.036Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ac/ac507a0a4ec478ca470440a09583db4be5259ba7670aeba0620822f1e57a/rpds_py-2026.9.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e", size = 349596, upload-time = "2026-10-04T16:30:24.558Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/f2/817a46b658d5070f477f722c298ee9a24525b0e4017347964146ef5fdd0e/rpds_py-2026.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96", size = 343114, upload-time = "2026-10-04T16:30:26.048Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/42/6ade976b13ac1b4cb3bf2eb603f1be2fe74df19a29988d4c2b386be59d6f/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445", size = 374197, upload-time = "2026-10-04T16:30:27.699Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/70/77cdf1d3f1a07faabe936016ae623aec7981f73108a8fe7a203ed2e21998/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69", size = 378025, upload-time = "2026-10-04T16:30:29.451Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/6b/18a44a3beaa9b7931acb04af7bd9539836477c630a794452d4826d6185d4/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495", size = 491969, upload-time = "2026-10-04T16:30:30.995Z" },
+ { url = "https://files.pythonhosted.org/packages/73/27/fb39cfd6bddaf741b024f813890374578ff8ac1f1adc473659c048b03b05/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c", size = 393905, upload-time = "2026-10-04T16:30:32.628Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/71/0fa7bb77b57af0d710273964180d11b503f62b8a5c358eb2d8c3f62feff6/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e", size = 374569, upload-time = "2026-10-04T16:30:34.257Z" },
+ { url = "https://files.pythonhosted.org/packages/54/22/f41cfac269af3b449513ef1bc3d7f32fde52abbbd2d01c7e76b47743acd9/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346", size = 377482, upload-time = "2026-10-04T16:30:35.997Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/fc/312b49006e7f8f9ca5f96647577b8aa6f3df30519c46bd448f5c425af0b2/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299", size = 400040, upload-time = "2026-10-04T16:30:37.76Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/a6/18cca7a878dc7fa95165a83343fd4d7b65643fd22e54e47340a451121d5c/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531", size = 551849, upload-time = "2026-10-04T16:30:39.443Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/6d/1f5685e20f39604691bdc3c05aaa6b8bd2f954e9e996477adf2376768e33/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb", size = 614842, upload-time = "2026-10-04T16:30:41.231Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/25/98652109fd9f7e10268dd4571aa52b81987001b806f37ef1a18260de714a/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa", size = 582606, upload-time = "2026-10-04T16:30:43.345Z" },
+ { url = "https://files.pythonhosted.org/packages/19/03/11ca09099bab5f53373a80a334c424ec917c13d050760a59499d2af5171e/rpds_py-2026.9.1-cp314-cp314t-win32.whl", hash = "sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569", size = 203341, upload-time = "2026-10-04T16:30:44.954Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8a/88909e3ffd9f47f5b58211473875d8c3c09079f0058c46fb72c55a702a26/rpds_py-2026.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e", size = 222843, upload-time = "2026-10-04T16:30:46.486Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/b7/a662f367d4896dd0a10cef2fc91f10b7f08af1c10858e287e019563f338d/rpds_py-2026.9.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9", size = 347463, upload-time = "2026-10-04T16:30:48.203Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3f/ad45d03df4f84ebae5439577ee81f3999d182711e82235c8037b6528890e/rpds_py-2026.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25", size = 342051, upload-time = "2026-10-04T16:30:49.872Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/31/3dcd68c13d4bcc59c1f7eb33ac8e80698f06f3eb0d8a1e06419836071c20/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871", size = 374143, upload-time = "2026-10-04T16:30:51.508Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/0d/68c1f058a250fbd1380ebda9fc227cbf50117adf8ffe8161ef383ea79f68/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91", size = 378604, upload-time = "2026-10-04T16:30:53.206Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/d6/2d4c59b85397cb4800594fadf692688ccf5ce556adc930e7a5bf21061a5e/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f", size = 490790, upload-time = "2026-10-04T16:30:54.925Z" },
+ { url = "https://files.pythonhosted.org/packages/da/04/7e05dc3aebaf52f4e026766bd668fdd09a9d0e23f64a14686b36b3501892/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d", size = 393146, upload-time = "2026-10-04T16:30:56.625Z" },
+ { url = "https://files.pythonhosted.org/packages/57/ca/e2e9a0a46a74ed51a0498ba1fe10f4ea6b2d9a155f372a2f91e51f18cf10/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945", size = 372022, upload-time = "2026-10-04T16:30:58.295Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/cb/8f8774df5134e23424372838bcc5c7ed4127d723e1ff52f7bebd4dcb2563/rpds_py-2026.9.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297", size = 377092, upload-time = "2026-10-04T16:30:59.984Z" },
+ { url = "https://files.pythonhosted.org/packages/90/02/8d7095d73bf9114219be40230baa5df00611e0a82ed9517779ff9c19f82b/rpds_py-2026.9.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0", size = 400830, upload-time = "2026-10-04T16:31:01.721Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/02/8206856f8f363cd042a8315dc86b3912f5dcb6d3c66bbb24b69c2bfb0775/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9", size = 551478, upload-time = "2026-10-04T16:31:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/41/6b/36211f1bb1f0b0313f496d92f5905b74ea107f27cb16fa3355a82f04575e/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6", size = 615173, upload-time = "2026-10-04T16:31:05.281Z" },
+ { url = "https://files.pythonhosted.org/packages/35/77/cda0c4a6f055446b692f0ed5692f73707cafd3dff82672ede31b1a9b59de/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7", size = 579653, upload-time = "2026-10-04T16:31:07.065Z" },
+ { url = "https://files.pythonhosted.org/packages/74/ec/d8385f446240aed643b9e92a5055cff3015cc04a13c61f73b2883c478ed5/rpds_py-2026.9.1-cp315-cp315-win32.whl", hash = "sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429", size = 206498, upload-time = "2026-10-04T16:31:08.989Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a5/71b5cd00e0521e3b6b81828baea368c62b6b700ebdd9554cd7d41ddf12fa/rpds_py-2026.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15", size = 223121, upload-time = "2026-10-04T16:31:10.84Z" },
+ { url = "https://files.pythonhosted.org/packages/33/58/dba857c3bc8221b31b62eb170a3080f4f191de79f047200389b7ed1b06a7/rpds_py-2026.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009", size = 218741, upload-time = "2026-10-04T16:31:12.592Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/d0/320ab28ccc1415eeb509d68682b0014fb74690cd49f1c2d29a232475af50/rpds_py-2026.9.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0", size = 349719, upload-time = "2026-10-04T16:31:14.48Z" },
+ { url = "https://files.pythonhosted.org/packages/56/88/f5b12f1358f443c08b7ce3cc8391d82d335f4872580e2e097847fd36087a/rpds_py-2026.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a", size = 343267, upload-time = "2026-10-04T16:31:16.827Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/0c/c765b0059d532acb3b9c45d781ccc22f15a96dbe443d00903f643ba9df10/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d", size = 374555, upload-time = "2026-10-04T16:31:18.931Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8a/cafddfda77564a10cd21184640c3bffda6a2b8d20972fb5dedd5e0166328/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db", size = 378346, upload-time = "2026-10-04T16:31:20.75Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/01/5e626016eff72c183bf6c96539240cace15d402468647a453ec08415b2fc/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250", size = 492373, upload-time = "2026-10-04T16:31:22.614Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/9c/15a2469e9389242f46896b3f0a01d68caea8a5a35c011fcb05ef333aae73/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f", size = 394371, upload-time = "2026-10-04T16:31:24.768Z" },
+ { url = "https://files.pythonhosted.org/packages/63/f5/c100ff77e1e6366e947c75969c258fdfc4b7bc5bbfe7351e62ffbf2a1228/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018", size = 375132, upload-time = "2026-10-04T16:31:26.588Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/f4/fe0269c9de253e99c81cabc12b8971a5feaa083debdaff1221e06264d9e3/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d", size = 377642, upload-time = "2026-10-04T16:31:28.677Z" },
+ { url = "https://files.pythonhosted.org/packages/05/65/b34a7b257baccff8f4a24a722933166d4941d5ebdc9c3f4bc4ffcd5ce4f4/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b", size = 400855, upload-time = "2026-10-04T16:31:30.802Z" },
+ { url = "https://files.pythonhosted.org/packages/98/32/844e54176b6071b90b38a564e6940bc6eb8f97b2890dc709c19db9dec0f4/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6", size = 552573, upload-time = "2026-10-04T16:31:32.709Z" },
+ { url = "https://files.pythonhosted.org/packages/17/73/6041d20729dffbfdf155c02d65be58bc225a1c1fb548fd87c23ef306138f/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad", size = 615840, upload-time = "2026-10-04T16:31:34.565Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9e/418094adaee6b056ce199051b255448ed872829051e341b2294c80da0977/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903", size = 582764, upload-time = "2026-10-04T16:31:36.665Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/9b/1698ebf6b840ddfe8b472198abbed6ace35c6e398faeecd6c47dae742a4e/rpds_py-2026.9.1-cp315-cp315t-win32.whl", hash = "sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b", size = 203795, upload-time = "2026-10-04T16:31:38.53Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/e2/91f70d804c61f8eac39a417e82aa1024f655ab9e8bdd7393b196242bc41b/rpds_py-2026.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567", size = 223037, upload-time = "2026-10-04T16:31:40.468Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/ad/f0eba548e297987964ef8088403db875b333963bf8189f3f563b6936b20e/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30", size = 384112, upload-time = "2026-10-04T16:31:42.643Z" },
+ { url = "https://files.pythonhosted.org/packages/33/89/891fa4d81f1eae7fe262ecd0aba2c51de94a116c7f3ccfb034fa75f4c537/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050", size = 389662, upload-time = "2026-10-04T16:31:44.642Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/46/912cf623b2263f9c9c2d04e8508fc38dbe89983faeba5fb36ca4558ae550/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb", size = 504637, upload-time = "2026-10-04T16:31:46.667Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/5c/7f3cfa7c5f01765c9f47c8398210ed0c1eb686c0e99c223cb1c6776d3fb9/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df", size = 402607, upload-time = "2026-10-04T16:31:49.068Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/76/2e915d4115ee453c33e6af17bc1df7e22ddc52974e178eeeb92a78c09983/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a", size = 382762, upload-time = "2026-10-04T16:31:51.24Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e5/844382925d7b84a4074ad79fc2b9d05eb6231ea243b34f247e68e519b606/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608", size = 389563, upload-time = "2026-10-04T16:31:53.549Z" },
+ { url = "https://files.pythonhosted.org/packages/81/82/d6e3951602dc2e24790ef9d299aa24bf5e2493df5043f2c6da00480cc447/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9", size = 416842, upload-time = "2026-10-04T16:31:55.76Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/1c/8d248e4ea671d525ab7daa0703ad81cc2175076fa0ccd7ef088facbd2b01/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c", size = 562434, upload-time = "2026-10-04T16:31:57.874Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/38/e7ac0db898120d15dea25e3661b1242a40cb3b23d1f4db86c15a01fe0ba1/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f", size = 627909, upload-time = "2026-10-04T16:31:59.977Z" },
+ { url = "https://files.pythonhosted.org/packages/03/c8/37e555514932cb1ee36c75349187aa882a98dee5d5654c761699ed8a0d27/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e", size = 593658, upload-time = "2026-10-04T16:32:02.14Z" },
+ { url = "https://files.pythonhosted.org/packages/12/5c/5f69a91add94a8a33acea68a2b034a464946a654ff7296af9d957ef0d6a5/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32", size = 351769, upload-time = "2026-10-04T16:32:04.675Z" },
+ { url = "https://files.pythonhosted.org/packages/41/22/35ea8bf3b2682b90ab687c74e4828418ff12298a5b0b0a0c033f61c86a30/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31", size = 351719, upload-time = "2026-10-04T16:32:06.834Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/90/a9ba81408369d34c1aca73319c15adc91ed45c1d21e9e8ee4832ffb7fe0b/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4", size = 381109, upload-time = "2026-10-04T16:32:08.994Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/44/5192a0bed94cec86bd2a5e1cc5a1bb8f7eec3aec907eacd2deb13e87e326/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759", size = 388222, upload-time = "2026-10-04T16:32:11.321Z" },
+ { url = "https://files.pythonhosted.org/packages/92/cd/5356549711448f18b52f7a11ffbe90f1228774996fbf3c6cd1b18d22abaf/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb", size = 500384, upload-time = "2026-10-04T16:32:13.478Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/88/ddda9d28adfe33119c75b2e733d4ba7e326f41d7e1b1093951771768ca48/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82", size = 401661, upload-time = "2026-10-04T16:32:15.653Z" },
+ { url = "https://files.pythonhosted.org/packages/32/c3/bb59ba16a6b4a57995d15b8c04c00f28df3b938c4dcdde48fbe0f73edb0c/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c", size = 380748, upload-time = "2026-10-04T16:32:17.727Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/b8/0580faa1c5a32ddc160130dd7ae54d2d007272a89a40a6b5db3e50f9f585/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14", size = 385731, upload-time = "2026-10-04T16:32:19.855Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/70/f73564642bbe3322c7eeef2c2f258cf040b71c41a430b532e59d04a1b838/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c", size = 412131, upload-time = "2026-10-04T16:32:22.285Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/5d/17fff2e1f8f68721a2afb5cb48f7e442c751bb6b4883157c5abaf618cd08/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68", size = 558193, upload-time = "2026-10-04T16:32:24.4Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/d0/869ab6fb08531f97aae4a780b4d61b4190d8aef48fe054ee28bcf7114466/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_i686.whl", hash = "sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da", size = 622448, upload-time = "2026-10-04T16:32:26.844Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/a5/9672e532fe02cd3b92c78cfd8743abca939d96853006c63835009a1ecb6f/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448", size = 591126, upload-time = "2026-10-04T16:32:29.274Z" },
+ { url = "https://files.pythonhosted.org/packages/80/ef/3a9f8e4279c7920af561deed4cb7ebebed2794a8f608cf404d5eef14a105/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_10_12_x86_64.whl", hash = "sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0", size = 357108, upload-time = "2026-10-04T16:32:31.641Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/55/4b2fa381a583760aea5c92841e4e928a358e1c6511b129219e9c2826a226/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37", size = 346877, upload-time = "2026-10-04T16:32:34.061Z" },
]
[[package]]
name = "ruff"
version = "0.16.10"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10.tar.gz", hash = "sha256:eff4728c4eaae93f0955cd264d24b2ab348e74bf59986ccf282ba6dc16b3b017" }
-wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-linux_armv6l.whl", hash = "sha256:488b0fe3f3574210e5cf80d9f59b9e3ab17a127a8155de3f307b392589cfb511" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e748ff95c934c4e978783b8e687bc174e7bd84e8ad24e3243e1ecfcda5e0282d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3031a4a2e8e7b8a46f70be45f198c35a11ece509a94b80334d8d397a33c67550" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:494401c86df4c4c25f69b9419605d944467ee98c42fb6ad405ef4fa40b8fb67d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d203abc0ff2b773ee33d00ab8df0bb67046fbc7c07b119332f08b9b345cf8221" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd83d1235a5258d318477bc5b576303974cbdef5df0c01a1bff14efcc23bd12a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc2610fb269fa56dd8a68669ae470fa6272902668c0fc2ebc3aa112b2633d5b8" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e70175e29cc94c26ea296c80e470180b744b7419026898e58f520c6ab32578e" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f33f43a864a8483eebd160e713336c8bab02c934feaff0a33cf5ccb41546d09a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1dfc6f0088149fb6a362c1c446bcbb3fd2157b3852fe2fa68409276eab9ad9b3" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6553498afc35f580f036030795810b9e6bcea31604b0fd9e8d352795473042e3" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a3b8471dea115d37f123882be852bed13403746d3a76c11de4a19ec5f9ff5a03" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:92e59a70bcbd9d3a5483656da906ec28edfdacfce00afd99edb8b4e9d15644be" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7ae7375f803b5520dc9f546bed7e3a0acb70b91812e9bb4b19927de22f25b77d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-win32.whl", hash = "sha256:2a12e01cb9156c10c466f63b46eaae5ecea28dfbd21b5836353ae498e7d1349a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-win_amd64.whl", hash = "sha256:97f2015c92aa97105b0eab19eb5d224884399281cfc5da86a92db4ab5e7fb2ca" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/ruff/0.16.10/ruff-0.16.10-py3-none-win_arm64.whl", hash = "sha256:25a65fe998c4e6861ec079ada5826a2fc605e6cbccbe9dcd7fac1f54e791621b" },
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c4/49/23802c45f093eb14bde54b141d2b2f058edfa63a06db7beed047308cc08f/ruff-0.16.10.tar.gz", hash = "sha256:eff4728c4eaae93f0955cd264d24b2ab348e74bf59986ccf282ba6dc16b3b017", size = 4958724, upload-time = "2026-10-01T18:03:21.697Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2f/21/ebce22e1d90cdb2cd691026b9c6e9bec6499f0b396089481755b5d49efff/ruff-0.16.10-py3-none-linux_armv6l.whl", hash = "sha256:488b0fe3f3574210e5cf80d9f59b9e3ab17a127a8155de3f307b392589cfb511", size = 10095557, upload-time = "2026-10-01T18:02:36.072Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/98/a54de85876a8b2612bfa0d84c7b9abfb39c6a3354aee7800b09c1649b9e3/ruff-0.16.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e748ff95c934c4e978783b8e687bc174e7bd84e8ad24e3243e1ecfcda5e0282d", size = 10340577, upload-time = "2026-10-01T18:02:39.258Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/16/1a5a4a2657effe4806110f2b907313802f1367fcdbb29e8122766e407fab/ruff-0.16.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3031a4a2e8e7b8a46f70be45f198c35a11ece509a94b80334d8d397a33c67550", size = 9774282, upload-time = "2026-10-01T18:02:41.79Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/fb/470085af734da396e68cd80fb0a3e7c109a459716ae59588c0f6fab8a17d/ruff-0.16.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:494401c86df4c4c25f69b9419605d944467ee98c42fb6ad405ef4fa40b8fb67d", size = 9920895, upload-time = "2026-10-01T18:02:44.458Z" },
+ { url = "https://files.pythonhosted.org/packages/57/de/f10cffe4f88a37ea6615ec460f01bf76f0bc1477737a35d9ee61a630a78b/ruff-0.16.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d203abc0ff2b773ee33d00ab8df0bb67046fbc7c07b119332f08b9b345cf8221", size = 9902707, upload-time = "2026-10-01T18:02:47.041Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/44/3fdcedf83ae60ef837dd239170e480dce606a9142cfa2db911afdf855fd9/ruff-0.16.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd83d1235a5258d318477bc5b576303974cbdef5df0c01a1bff14efcc23bd12a", size = 10619287, upload-time = "2026-10-01T18:02:49.485Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/62/02e76a5574002153618eafbb70e159468addc72a5d2c488e65cbb4639d2d/ruff-0.16.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc2610fb269fa56dd8a68669ae470fa6272902668c0fc2ebc3aa112b2633d5b8", size = 11339942, upload-time = "2026-10-01T18:02:52.008Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/c4/cde27d47ad8d4126c587e608c67c46e7feac11763f606d261a1bc8a489e6/ruff-0.16.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e70175e29cc94c26ea296c80e470180b744b7419026898e58f520c6ab32578e", size = 10934316, upload-time = "2026-10-01T18:02:54.811Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/03/17234145f302a645a123e8c3bb2411ecf4669fbc350de3b0d803230a1729/ruff-0.16.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f33f43a864a8483eebd160e713336c8bab02c934feaff0a33cf5ccb41546d09a", size = 10387968, upload-time = "2026-10-01T18:02:57.497Z" },
+ { url = "https://files.pythonhosted.org/packages/71/29/2493af60240ee7644b38a4b821f5f9c3b5a4fa3178770fe1d0c685217395/ruff-0.16.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1dfc6f0088149fb6a362c1c446bcbb3fd2157b3852fe2fa68409276eab9ad9b3", size = 10537906, upload-time = "2026-10-01T18:03:00.006Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/9a/f56b28f3b143bb9e518c34fb89ab191b626bca8b70dbf8af0d2e2d473572/ruff-0.16.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6553498afc35f580f036030795810b9e6bcea31604b0fd9e8d352795473042e3", size = 10012938, upload-time = "2026-10-01T18:03:03.006Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c4/fca37362848ea4d4d80712df13632e645e7c7cfb1bdedf140699ac7a090b/ruff-0.16.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a3b8471dea115d37f123882be852bed13403746d3a76c11de4a19ec5f9ff5a03", size = 9897945, upload-time = "2026-10-01T18:03:05.818Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/c7/e0bc57664d6e0c61fd22f620af260fe9662d7e1ddb320ea7f160e76ef165/ruff-0.16.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:92e59a70bcbd9d3a5483656da906ec28edfdacfce00afd99edb8b4e9d15644be", size = 10333134, upload-time = "2026-10-01T18:03:08.25Z" },
+ { url = "https://files.pythonhosted.org/packages/21/aa/5c9f3b68737c0e4a33a1db5dfab44f7b786d96ca91a7233112ddab1e6dc2/ruff-0.16.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7ae7375f803b5520dc9f546bed7e3a0acb70b91812e9bb4b19927de22f25b77d", size = 10741702, upload-time = "2026-10-01T18:03:10.775Z" },
+ { url = "https://files.pythonhosted.org/packages/78/fa/0f9c2020dc316c53d983be011720b8157cc052b97e3f4dfa7db6d0f880a6/ruff-0.16.10-py3-none-win32.whl", hash = "sha256:2a12e01cb9156c10c466f63b46eaae5ecea28dfbd21b5836353ae498e7d1349a", size = 10139176, upload-time = "2026-10-01T18:03:13.267Z" },
+ { url = "https://files.pythonhosted.org/packages/99/29/cfb0df9448d4d4ad48c2de029ada9ebd71baa6da983a6c77ee6c6cd0fe82/ruff-0.16.10-py3-none-win_amd64.whl", hash = "sha256:97f2015c92aa97105b0eab19eb5d224884399281cfc5da86a92db4ab5e7fb2ca", size = 10584734, upload-time = "2026-10-01T18:03:16.006Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/05/c16957eb287c3fc062e032619a25868d93d408a844b725bcf514f7a378ff/ruff-0.16.10-py3-none-win_arm64.whl", hash = "sha256:25a65fe998c4e6861ec079ada5826a2fc605e6cbccbe9dcd7fac1f54e791621b", size = 10366440, upload-time = "2026-10-01T18:03:19.04Z" },
+]
+
+[[package]]
+name = "s3transfer"
+version = "0.19.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/49/c5/0a5ce655109cb96b8dfad36908a4e108d15f5dc2eff8fbc3146c4312e704/s3transfer-0.19.3.tar.gz", hash = "sha256:8326acc3d056d7f7b8a37110c7d2aea41f67e83f6ae79cd3b5fbcca34c726406", size = 166087, upload-time = "2026-10-19T19:19:49.751Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e2/a4/88f9e10498759121060b8050a1554753dfc04df39bbbcc7aad9ecc8d9440/s3transfer-0.19.3-py3-none-any.whl", hash = "sha256:24eb7535804306355e31c952a90022bf5b2f9d760f9a11062c7493a217c08553", size = 90252, upload-time = "2026-10-19T19:19:48.309Z" },
+]
+
+[[package]]
+name = "scantree"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "pathspec" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/e4/40998faefc72ba1ddeb640a44fba92935353525dba110488806da8339c0b/scantree-0.0.4.tar.gz", hash = "sha256:15bd5cb24483b04db2c70653604e8ea3522e98087db7e38ab8482f053984c0ac", size = 24643, upload-time = "2024-08-03T20:08:59.413Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/ce/828467ddfa0d2fe473673026442d2032d552a168e42cfbf25fd0e5264e0c/scantree-0.0.4-py3-none-any.whl", hash = "sha256:7616ab65aa6b7f16fcf8e6fa1d9afaa99a27ab72bba05c61b691853b96763174", size = 20690, upload-time = "2024-08-03T20:08:58.137Z" },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
+]
+
+[[package]]
+name = "shortuuid"
+version = "1.0.13"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/e2/bcf761f3bff95856203f9559baf3741c416071dd200c0fc19fad7f078f86/shortuuid-1.0.13.tar.gz", hash = "sha256:3bb9cf07f606260584b1df46399c0b87dd84773e7b25912b7e391e30797c5e72", size = 9662, upload-time = "2024-03-11T20:11:06.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c0/44/21d6bf170bf40b41396480d8d49ad640bca3f2b02139cd52aa1e272830a5/shortuuid-1.0.13-py3-none-any.whl", hash = "sha256:a482a497300b49b4953e15108a7913244e1bb0d41f9d332f5e9925dba33a3c5a", size = 10529, upload-time = "2024-03-11T20:11:04.807Z" },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
+]
+
+[[package]]
+name = "starlette"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ad/eb/4e197bf26b023abafc5e9043ec8710d7de0fcb032d0969045236d72aa0b1/starlette-1.9.0.tar.gz", hash = "sha256:3d931de246ed870e35ce605974440816ef895e592f255d1396cfdf191d4c4318", size = 2732466, upload-time = "2026-10-18T15:50:33.189Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f3/93/181df62699d9b5aca1d87e5cfda131dac87e3cdaebf668662f7206ca3470/starlette-1.9.0-py3-none-any.whl", hash = "sha256:fd3f159936e882d06c1108f84688f7cda2cf85fc6a592a50edbd175e882c2ddc", size = 80335, upload-time = "2026-10-18T15:50:31.526Z" },
+]
+
+[[package]]
+name = "storage3"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "deprecation" },
+ { name = "httpx", extra = ["http2"] },
+ { name = "pydantic" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/21/09/8ffcd57f51542f7d1eb917488de2255c879227d363bea764ae0501d775ad/storage3-2.33.0.tar.gz", hash = "sha256:7b5a27ca1e64e324420ca9514c38317f243a0ae7a56aeef336976034c1c565d4", size = 20166, upload-time = "2026-10-15T19:14:21.516Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5c/78/98ff52ddd24177ca72e0bbc7527fc8391df187304f32de2421d20b55f1b2/storage3-2.33.0-py3-none-any.whl", hash = "sha256:6e357ea7328961bcecc02a35fac8fae555b0c4b6b02ff553a956824693fd4cec", size = 28469, upload-time = "2026-10-15T19:14:20.459Z" },
+]
+
+[[package]]
+name = "strenum"
+version = "0.4.15"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/85/ad/430fb60d90e1d112a62ff57bdd1f286ec73a2a0331272febfddd21f330e1/StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff", size = 23384, upload-time = "2023-06-29T22:02:58.399Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/81/69/297302c5f5f59c862faa31e6cb9a4cd74721cd1e052b38e464c5b402df8b/StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659", size = 8851, upload-time = "2023-06-29T22:02:56.947Z" },
+]
+
+[[package]]
+name = "supabase"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx" },
+ { name = "postgrest" },
+ { name = "realtime" },
+ { name = "storage3" },
+ { name = "supabase-auth" },
+ { name = "supabase-functions" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/ef/67310d1a638c36cbe09fe73afc5b9f09e9cd5b9b55e9744d90e35e6be4a1/supabase-2.33.0.tar.gz", hash = "sha256:7834e2b6373773c8105449cec3089a0dc1bc4069505be057a13404cfcbc42f8c", size = 9820, upload-time = "2026-10-15T19:14:23.505Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/48/1a8f57fdaf882f1dcd7836352e33829c1d455c6cfc387d2b0fb1e99ffdb6/supabase-2.33.0-py3-none-any.whl", hash = "sha256:57c3aeeac3d612321deac8176b935077a7643461c8df4bf28722c2024c87ccc6", size = 16753, upload-time = "2026-10-15T19:14:22.26Z" },
+]
+
+[[package]]
+name = "supabase-auth"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx", extra = ["http2"] },
+ { name = "pydantic" },
+ { name = "pyjwt", extra = ["crypto"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/9b/a6093da7497561900a1102958a7d02d694441704ea4bc89d0f87a3229a33/supabase_auth-2.33.0.tar.gz", hash = "sha256:832d3097bad72cf5bf70b55ab776385f8bad2c17a9c37b858e8bb7a6abe7c4a8", size = 39316, upload-time = "2026-10-15T19:14:25.522Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f5/cd/8fadb81917823bf416290fad4e652f44357572b52c8ce86be0d26bd19dab/supabase_auth-2.33.0-py3-none-any.whl", hash = "sha256:d48f361309cb5e8eeb7babfd0f326e1a81870f27a0ba15f94194fbab13153f5c", size = 48534, upload-time = "2026-10-15T19:14:24.247Z" },
+]
+
+[[package]]
+name = "supabase-functions"
+version = "2.33.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx", extra = ["http2"] },
+ { name = "strenum" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/42/de737739a908cbcdc86389f4d5ee2ca12abaa780e1560c50d290a1113739/supabase_functions-2.33.0.tar.gz", hash = "sha256:1563788b6d15c29c1aee77c7e6fbdb1a8f3e29e4a8b3231e82e01f825ed00acd", size = 4996, upload-time = "2026-10-15T19:14:27.352Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/4e/30a13a3b58a97828faaf9df2af0947f9ff4a2585289dcd17b2c95f6d2c72/supabase_functions-2.33.0-py3-none-any.whl", hash = "sha256:3a8531f5ac7519535fb0032fa4490220d49d338e83608d38d1b178e9c7999414", size = 9269, upload-time = "2026-10-15T19:14:26.253Z" },
+]
+
+[[package]]
+name = "tenacity"
+version = "9.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/9e/497c1c8ebe5a5b5d1d4a7511aea22c0bb1a97e3170d98abdef0e1b34265a/tenacity-9.2.1.tar.gz", hash = "sha256:a606b5c808d0cded4a359d5b9932d867ff2a6a6b64d37350260fd01bbdf83839", size = 58261, upload-time = "2026-10-07T12:13:01.633Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/26/1ff2b0721ac66a3ec5b1402b333110b352ab0a8724052ac279a7b82d40c4/tenacity-9.2.1-py3-none-any.whl", hash = "sha256:9e56f17539296baab7beabb08b92f6ee3d7be92d8be72d763360677c2ad6580e", size = 32310, upload-time = "2026-10-07T12:13:00.102Z" },
+]
+
+[[package]]
+name = "tiktoken"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "regex" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/62/167a842aa0429d45f5e797354fd4343a96f6043d67d0513c675c7b8d36e6/tiktoken-0.14.0.tar.gz", hash = "sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874", size = 38898, upload-time = "2026-08-17T19:49:49.514Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5e/82/d60a7a5d7bff7b4641d556ea68ea5914ea6edc3774a12eb1c0d444701382/tiktoken-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91", size = 1095817, upload-time = "2026-08-17T19:48:31.788Z" },
+ { url = "https://files.pythonhosted.org/packages/18/e2/d39ae33d3dc30a0c229ff0cb683df961ebb5e7b8691feb2d08b3ee6ac327/tiktoken-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1", size = 1043064, upload-time = "2026-08-17T19:48:33.138Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e9/8e18cbee0c3ae8321c7e9696bef6090a24eed99a4a75a4c4a7f5115e5a2f/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c", size = 1190381, upload-time = "2026-08-17T19:48:34.386Z" },
+ { url = "https://files.pythonhosted.org/packages/af/c8/051e7b72a816ff50eb34a1c7c5b185cd2429ffdf59a497baea35b2b6b2dd/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7", size = 1206869, upload-time = "2026-08-17T19:48:35.581Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/b3/7795db206adb6a57d6137fe48ef2cca6b9707e90b86ee8244671592ddc33/tiktoken-0.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33", size = 1255197, upload-time = "2026-08-17T19:48:36.832Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/39/5234783af6b81af645ccdf9438f2f02af472f14e91d876ca2079af641841/tiktoken-0.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14", size = 1319329, upload-time = "2026-08-17T19:48:37.944Z" },
+ { url = "https://files.pythonhosted.org/packages/88/cf/f2d955c8c5c6c67cc86ba6fb132c47c710465ebe6a6dcec1c3b6e250660e/tiktoken-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c", size = 944146, upload-time = "2026-08-17T19:48:39.011Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/c5/9d848b7f408241171e1f843deb8bfa626086452bc9c78beee500829583e3/tiktoken-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79", size = 1094971, upload-time = "2026-08-17T19:48:40.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/a9/d94302340304328961d6f0c35ca4e60617fbb57a5cf667e2ed1692cb9e57/tiktoken-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948", size = 1042916, upload-time = "2026-08-17T19:48:41.541Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/b6/31da98ee871383509cae2ba96a9ddef1965e3c4f8cb6dc7bcda3379398db/tiktoken-0.14.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f", size = 1188650, upload-time = "2026-08-17T19:48:42.729Z" },
+ { url = "https://files.pythonhosted.org/packages/24/65/8c5dddd7cb67f6571d154a58d7c6e2f07da54bf84c49b6a1839965b7c35e/tiktoken-0.14.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513", size = 1206378, upload-time = "2026-08-17T19:48:44.013Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/04/522ec59d30dd9a2f3ab837011cd4fc5d1178dc4a2fa07c9fa4b90af6ba9d/tiktoken-0.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78", size = 1253694, upload-time = "2026-08-17T19:48:45.597Z" },
+ { url = "https://files.pythonhosted.org/packages/69/84/9019e272bad188a1c61ecf44f25a9ba2368744644e3ac1f3d6516f3c9e80/tiktoken-0.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e", size = 1317873, upload-time = "2026-08-17T19:48:46.792Z" },
+ { url = "https://files.pythonhosted.org/packages/24/7f/fff1217240343c0c11b5938b98aeae0e3a266cacfac25f86f91cdcd748f0/tiktoken-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da", size = 944395, upload-time = "2026-08-17T19:48:48.028Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/da/e273746b9d24a63c776bc60fba914351573ad9c575b52601eb5e60632564/tiktoken-0.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36", size = 1094408, upload-time = "2026-08-17T19:48:49.269Z" },
+ { url = "https://files.pythonhosted.org/packages/69/9f/fe6b1aca23331aa5271df5a4bd07bf68a7059254d47faee1b8272592a777/tiktoken-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4", size = 1038499, upload-time = "2026-08-17T19:48:50.666Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/35/e9f47647c9e163bd1de30fe1a491669b7248cfc67b7404c35c009a701e1a/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6", size = 1186355, upload-time = "2026-08-17T19:48:51.93Z" },
+ { url = "https://files.pythonhosted.org/packages/51/11/9976ad86980a00cdef05e730a0127a2578a1bc6d11644d8d47246de2eb26/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d", size = 1204197, upload-time = "2026-08-17T19:48:53.18Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/9c/7035b0bcfaa68d1ee4803fc5be5214ad865669b05bd20e7105ae8a18afc6/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482", size = 1250635, upload-time = "2026-08-17T19:48:54.392Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/1d/69cabf18bed7f4366da076735816abce0d4db3fae491ae338a6612128777/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6", size = 1316085, upload-time = "2026-08-17T19:48:55.525Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/bd/a2e884fb1402cba5be08836590320012b2d8ada0e2eef9911a64df4bcd2d/tiktoken-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3", size = 941208, upload-time = "2026-08-17T19:48:56.938Z" },
+ { url = "https://files.pythonhosted.org/packages/50/53/ee1453623bf65f019328721ccb6587846d2c5b7b82f34e73ca09101f072e/tiktoken-0.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f", size = 1094198, upload-time = "2026-08-17T19:48:57.955Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/5f/6448cfe278c3664ba9ec5b5ac08344341f7dc3d42888476e215a14eda2be/tiktoken-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94", size = 1038820, upload-time = "2026-08-17T19:48:59.015Z" },
+ { url = "https://files.pythonhosted.org/packages/69/3b/d67eac1bcce9dee3abe23aff5e3ded3116bbebaf67b80a0811c06d3806fc/tiktoken-0.14.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06", size = 1186175, upload-time = "2026-08-17T19:49:00.068Z" },
+ { url = "https://files.pythonhosted.org/packages/37/62/cae690d9783146b0f81f564ada0f8f611de68178c0c9c7e1e969f0516b48/tiktoken-0.14.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d", size = 1203884, upload-time = "2026-08-17T19:49:01.163Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/1e/633e30237b94e383cf814145499079f3bb9cdd4aeafc1bc42e01b0f810a6/tiktoken-0.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010", size = 1250980, upload-time = "2026-08-17T19:49:02.274Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/56/4c12f07b812f84206f38d723eb1ebfdd34bad9309b5dbc0bee6bbcff4cbf/tiktoken-0.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632", size = 1315434, upload-time = "2026-08-17T19:49:03.434Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e0/c65603f0c44811def666d3fbf611bf2af3b5e1ef613e06c19411419830b3/tiktoken-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1", size = 940883, upload-time = "2026-08-17T19:49:04.583Z" },
+ { url = "https://files.pythonhosted.org/packages/59/b0/1cf129f4af8fc513931f931023def596b7c4bfc77026513cd9d851da9e88/tiktoken-0.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450", size = 1096273, upload-time = "2026-08-17T19:49:05.807Z" },
+ { url = "https://files.pythonhosted.org/packages/62/85/2ae74575e321148484147e10b53c3b1717c59ebaa9edb4fe18b1f5c055f8/tiktoken-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b", size = 1040269, upload-time = "2026-08-17T19:49:06.943Z" },
+ { url = "https://files.pythonhosted.org/packages/89/29/92a1120a12e4bcf2d5464350d1a91b68a433d63ce656bb7f806c27aec09c/tiktoken-0.14.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e", size = 1186101, upload-time = "2026-08-17T19:49:08.102Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/7d/144af98dc5ad68108451a82e2f5a17f80e2663f5115058b8dfd215c1ad02/tiktoken-0.14.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42", size = 1204457, upload-time = "2026-08-17T19:49:09.28Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/1f/be7cb06ab2108f612f3e92e7b76cf391e192db0db37a984616f0cc32aafc/tiktoken-0.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c", size = 1251716, upload-time = "2026-08-17T19:49:10.509Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/6b/81f158d0f90adb826cd704069c2129a046cb784a2a09861009519fc41cf4/tiktoken-0.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771", size = 1315432, upload-time = "2026-08-17T19:49:11.844Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/ec/f5fa35ec13f07279fdcaf3cc9c04bbb154ea591d23978651f2b672593e8a/tiktoken-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098", size = 988046, upload-time = "2026-08-17T19:49:13.282Z" },
+ { url = "https://files.pythonhosted.org/packages/68/c9/7756717408d3d0dfea3f046c9466144b28afde39ff69d5808f2475dcd7f5/tiktoken-0.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438", size = 1096261, upload-time = "2026-08-17T19:49:14.351Z" },
+ { url = "https://files.pythonhosted.org/packages/79/29/46ad8061f57bd9f8b2ea0aa82bf574e0f2aa040b0857a1582adba9957899/tiktoken-0.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa", size = 1040183, upload-time = "2026-08-17T19:49:15.707Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/7c/3184d17b868456f17b60b1a75f5ec0405618a43aa753336df341d8f11781/tiktoken-0.14.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037", size = 1186719, upload-time = "2026-08-17T19:49:16.84Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/e8/46de4400d5bf859f640feee85bd7e32235f68ddf25db53c63be78e581e3a/tiktoken-0.14.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef", size = 1204660, upload-time = "2026-08-17T19:49:17.987Z" },
+ { url = "https://files.pythonhosted.org/packages/29/ce/af8964c38bc8226dd8950305b7a255fa33345d5572f78af7275a313d28e0/tiktoken-0.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a", size = 1250932, upload-time = "2026-08-17T19:49:19.28Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/4b/323631116fc986d9cc5bbeb2b8223c7c85e61a8bb94ea5ab4951023b149b/tiktoken-0.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58", size = 1315190, upload-time = "2026-08-17T19:49:20.467Z" },
+ { url = "https://files.pythonhosted.org/packages/18/8b/ba48a73729c9270989b36f37ab2ed5525e52690d715097c9fa791aaa5d05/tiktoken-0.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0", size = 987717, upload-time = "2026-08-17T19:49:21.704Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/10/b73b7e319179e0f60b32475f783b044f9cece872c53b6662664e9084b0d0/tiktoken-0.14.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232", size = 1096280, upload-time = "2026-08-17T19:49:22.779Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/6b/09999a9bf1d559670d1680e8f8e419ac0e2c5f6aac82e9bfdf70f260b30a/tiktoken-0.14.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695", size = 1040433, upload-time = "2026-08-17T19:49:23.998Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/7b/8537be0836f3df99b2a636b44399bfa43cd757f2b8b4097dacb794cf24a7/tiktoken-0.14.0-cp315-cp315-manylinux_2_28_aarch64.whl", hash = "sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49", size = 1186989, upload-time = "2026-08-17T19:49:25.021Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/9d/f9c56d7a943a4468abf9ef37661bb9b8e0cd3aa8aa87368c7146cc3f3222/tiktoken-0.14.0-cp315-cp315-manylinux_2_28_x86_64.whl", hash = "sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4", size = 1204615, upload-time = "2026-08-17T19:49:26.37Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/d2/98a38579db25c4a8a84e31dd95d9072ec5f21f7e70de591da0412e29b25b/tiktoken-0.14.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871", size = 1251828, upload-time = "2026-08-17T19:49:27.423Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/83/467be424746c039c5493c0f4102feab16b9b48eb6f5c089b2a2438e3cde2/tiktoken-0.14.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f", size = 1316260, upload-time = "2026-08-17T19:49:29.101Z" },
+ { url = "https://files.pythonhosted.org/packages/02/ee/ddf46ca78e371f5890e96b6e7d089a85b3536432be219851eb0481786ca8/tiktoken-0.14.0-cp315-cp315-win_amd64.whl", hash = "sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea", size = 988230, upload-time = "2026-08-17T19:49:30.246Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/00/5162e90c851a28da18ed382d34898b79a8022548e5619a64e14c03ce7c3d/tiktoken-0.14.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890", size = 1096186, upload-time = "2026-08-17T19:49:31.656Z" },
+ { url = "https://files.pythonhosted.org/packages/65/97/a5a7bfccf25b1bb65e82bae8edff11ac3c9c041c374b7b4a823d60c38133/tiktoken-0.14.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5", size = 1039947, upload-time = "2026-08-17T19:49:32.848Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/ba/ef427fc638f1439181c5e12dd26b70e881861f89c007aa7e5b36300f8342/tiktoken-0.14.0-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae", size = 1186997, upload-time = "2026-08-17T19:49:34.121Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/88/2f3f85a968cdc514152129af0a060ebcccb067005a2f29b0d5ef3c838514/tiktoken-0.14.0-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1", size = 1205211, upload-time = "2026-08-17T19:49:35.284Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/f6/80760e98a08e6649d2d68afb6035af713121dfb615acce8c4f73810ec438/tiktoken-0.14.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89", size = 1251479, upload-time = "2026-08-17T19:49:36.419Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/84/50966fb6918a0fb9b32721277e5342bf729a2d74350074d662fbedf9772e/tiktoken-0.14.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3", size = 1316673, upload-time = "2026-08-17T19:49:37.756Z" },
+ { url = "https://files.pythonhosted.org/packages/35/5e/9b01afd037bfa22a0033963fa091e0f75b6fb15cd85bffb42ff86e697323/tiktoken-0.14.0-cp315-cp315t-win_amd64.whl", hash = "sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9", size = 987929, upload-time = "2026-08-17T19:49:38.947Z" },
+]
+
+[[package]]
+name = "tokenizers"
+version = "0.23.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "huggingface-hub" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/7c/2cabb2174e772636683008f2c5621949b645da7d303c596589e84516a184/tokenizers-0.23.3.tar.gz", hash = "sha256:cded33237c77caeef62944d32aa9a7ef42bdce2b3497e18d137e072a8c4be438", size = 385286, upload-time = "2026-10-09T10:16:55.759Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/2e/4ce5b9716f26e526eff6b0502ebed4ea8d7161f03b3c77617c9f25528e97/tokenizers-0.23.3-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9d2b5c97daf61688c2ad1803ca851800feaba50fb68d5821779e9ea5880d968c", size = 3148800, upload-time = "2026-10-09T10:00:51.457Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/72/01e49f032bb346e5aaf06c10c74fe8aeec847173adbadd66eb7c53054bf2/tokenizers-0.23.3-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:68649e97d5b43c44c031d8d848874a6eecae8f8fe40ea989aa777a5a83aca716", size = 3101381, upload-time = "2026-10-09T10:00:54.063Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fc/ae987741829b1cd547668c4c94be732ae3eefd1d74344e64c3d2ca714acd/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec82e80e65a862275b97c3d90b7a523df8d9519ee48aeb4e9625b2cc909274e0", size = 3519944, upload-time = "2026-10-09T10:00:55.885Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/da/cc8f6c030afaf05fbddc608158fbb761dca46913cbeba6b112e59fc82e2a/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c64a0713180ff16829d4e7f39a658b77ea11443af4e1aa46523692943c9b1414", size = 3397695, upload-time = "2026-10-09T10:00:57.444Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/f1/256f78d1365fa2cd3ea6db716883d74667c8cbb6a21f15fa5b89a773cdc2/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddedfd4b3b4be6be24ff6ca645c4a37fddfd305f6f3e354c54cf10b715c48215", size = 3753125, upload-time = "2026-10-09T10:01:00.165Z" },
+ { url = "https://files.pythonhosted.org/packages/60/93/eee007ac2fcbf4ecfce7fbc354826cf3611f56bdb886f3e91b1f7dd06b8f/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a89614730d7b80940a5d2ed9320e1ec8add5a745c6151d8d05071b7215505b6", size = 4018598, upload-time = "2026-10-09T10:01:02.05Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/f9/0c96c4739461fce9d8d865b416728081bf6230022d7163bd6244f35f4b31/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e88646b8580c5ad7f4361477f1298e9cc01771a1ee9aecfe32c47b8ff614cc38", size = 3602442, upload-time = "2026-10-09T10:01:03.77Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/40/6706b82693715581457c6d5423eaa7faae576bb0526c5738a57085eb4449/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:376851d22bcf9d650a5c3090bb83e6cf9e895fbf0595369fa4cd43c1f69b5f87", size = 3396193, upload-time = "2026-10-09T10:01:05.48Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/0c/85946de40e25b7364b8f1bcf56def129069acd5bb364b7c86a32919e1a23/tokenizers-0.23.3-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:bf501c40b72d2d5c8623620210430e9cac1ce47a46e45b34107b70a1557d46b0", size = 3553483, upload-time = "2026-10-09T10:01:07.387Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/6b/8d615d92cad1d511ca5ab188d1c7c167f0b3d295cc0d96207f9f82d486d8/tokenizers-0.23.3-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:114e2b55ed177179d59f4ab98200a4471e11e78f9e4b5a922d146740f96fcf52", size = 9972248, upload-time = "2026-10-09T10:01:09.437Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/7d/a922e37ddd58d1b463bbc2ad08120c8f59c60b814cd353519a116b24f8ba/tokenizers-0.23.3-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:d3407fb7b9c4d75dd68850ffd7180bc0a5d2dbaf0762d888e612f31fec3f9c6b", size = 9802957, upload-time = "2026-10-09T10:01:11.869Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/06/5d3f506a86ae0699a0e4ea05c05978f9aee169ef2c1d844e68c971cf8194/tokenizers-0.23.3-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:84513ef0aeb8bf8f4ea11a2e8a7ac163ec5288aa115e649a59b470ac5c3107df", size = 10145487, upload-time = "2026-10-09T10:01:14.268Z" },
+ { url = "https://files.pythonhosted.org/packages/26/e5/065625317690ea3548d834dad81f48ea1fd32e4964610e658e195d7fe28e/tokenizers-0.23.3-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e05ab7baf7f47b406a95fea6f3b0a484b2ddcd9e1d14b68844c457eb755085a3", size = 10266026, upload-time = "2026-10-09T10:16:33.054Z" },
+ { url = "https://files.pythonhosted.org/packages/77/4e/babede85d0d19f5e3deeef0063e01848141329934d3d77c31b5cab5ac2b4/tokenizers-0.23.3-cp310-abi3-win32.whl", hash = "sha256:1ebf28794e7e4954e20a7f70fbea410b2d1f0418f7dbbca97ca384fcfef38c25", size = 2588086, upload-time = "2026-10-09T10:16:35.686Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/6c/24f074c9a0efb98e61b20aafe6b2641922d5db24e447d5d6daffd9e17555/tokenizers-0.23.3-cp310-abi3-win_amd64.whl", hash = "sha256:1f0823bb00c5fdc98e487354d54dd55a03848d61a1a0bf29a68c77f24f3b26c3", size = 2872101, upload-time = "2026-10-09T10:16:37.533Z" },
+ { url = "https://files.pythonhosted.org/packages/53/77/a476b6f73a661c11d113a342d2326b91506cf2285f0995d1212a6bb2022d/tokenizers-0.23.3-cp310-abi3-win_arm64.whl", hash = "sha256:7e48734d2de9260d86f03ab056d2cfeeff3869f61dbd49aaa15a2793b5f3458b", size = 2742580, upload-time = "2026-10-09T10:16:39.244Z" },
+ { url = "https://files.pythonhosted.org/packages/65/46/f66baaedd42414a3f583c47379dc350e3e1f858a690d2574fd85ae70681b/tokenizers-0.23.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:efa3d7318406b4d115dce61ad5061953f1f44b128e79c020ce4615d763e23b6e", size = 3154274, upload-time = "2026-10-09T10:16:40.876Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/41/8de8c63b2d935eee5a0f42011fb7b786ffafeab0b8eb6d17acb8af2293b7/tokenizers-0.23.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a4fbb3662f9f59d199d61338e54b4bcc11d07ebbb1aeb3540dacb2be9c521cb7", size = 3077805, upload-time = "2026-10-09T10:16:42.856Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/08/b1cbae8dc8fc7c91f992ac2d87a086e9b3f25a28814047ca16a82fe8c87b/tokenizers-0.23.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de536665495cb4b409d25bade41963f801aff4225c19a6b804b048f7d14e34c7", size = 3491678, upload-time = "2026-10-09T10:16:45.093Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/0d/aac0cb2f3a1fdbef514145b4c5f2df4d05deeb1ee8f73ae641a1b4a62a85/tokenizers-0.23.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc24bb457dd4a8af89c8fcb40074d570129ec473df2a866c276ee55db4749d7", size = 3367420, upload-time = "2026-10-09T10:16:47.112Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/1d/41a697d0c193a320b243fbd68b2057b6eb2f01ecf80899e1a16e646ff699/tokenizers-0.23.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:acd5c57b4bd3e56e246e2731a3a3a6825a7a7d89b7e3b761ba80bc521710f04b", size = 9945973, upload-time = "2026-10-09T10:16:49.326Z" },
+ { url = "https://files.pythonhosted.org/packages/37/e9/b56e619fcd583000a2b1254bb46af8dc6a174d3ba3329f454ad5a95a2be2/tokenizers-0.23.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:82eb480f6f1c21cea3349dec32cf1a6384c6c1e775f00f83b0d51197bc013687", size = 10237491, upload-time = "2026-10-09T10:16:51.943Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/68/f58b3beb95f3b62816e91e5e768e684cd63e58f9cbece22036dae3b1c971/tokenizers-0.23.3-cp314-cp314t-win_amd64.whl", hash = "sha256:1554a6eed34d9d6a78d23360f4e06df8dffab1ae08c7e8488e0b3e3b36cc266f", size = 2847654, upload-time = "2026-10-09T10:16:54.166Z" },
+]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
]
[[package]]
name = "tomli"
version = "2.4.1"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f" }
-wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396" },
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/tomli/2.4.1/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe" },
+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 = "tqdm"
+version = "4.70.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/ea/b2a5bd54b28a324dae8211928b2d730b6547500342c7e6c6dea08bd0a485/tqdm-4.70.1.tar.gz", hash = "sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4", size = 171846, upload-time = "2026-09-11T07:25:16.601Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl", hash = "sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73", size = 80199, upload-time = "2026-09-11T07:25:14.599Z" },
+]
+
+[[package]]
+name = "typer"
+version = "0.27.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "rich" },
+ { name = "shellingham" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/03/51/d33db42cc72ffd8c30777547b42d01f0cbf9d95a770457698d0174b3ed71/typer-0.27.3.tar.gz", hash = "sha256:d0396f770a560ab1b0a8504e13b5f254b728cedb05c61cf0359e944e50ce8901", size = 205303, upload-time = "2026-10-06T17:24:16.61Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/ea/2e31b67051e91a133189e9c000c222502ddc6969856416de0d095de4c0b0/typer-0.27.3-py3-none-any.whl", hash = "sha256:e50022f28b82a86313e54501317a1db64bf8f8d036ff8cfe5ca7e47675454aff", size = 123312, upload-time = "2026-10-06T17:24:15.054Z" },
]
[[package]]
name = "typing-extensions"
version = "4.16.0"
-source = { registry = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/simple/" }
-sdist = { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/typing-extensions/4.16.0/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5" }
+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.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.54.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/da/34/30e9280707135d2cfc589dfff3cb796bd07a3aeb1a3e415ba09dd89d7bb4/uvicorn-0.54.0.tar.gz", hash = "sha256:a2e33cbfaa0306f8e6b0c13e0cb89d7d7a2da3e62b90c66e18c33d9807b28620", size = 112283, upload-time = "2026-09-25T06:52:37.601Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/0c/b54a4fdd7f90a3af8b02ebc9ce6712c2c208b7926a2f7bad95c33ebbe943/uvicorn-0.54.0-py3-none-any.whl", hash = "sha256:505bdb0f318731d45f1f712071fc781a8981f6847a31c902c9f5e652d4f67faf", size = 87427, upload-time = "2026-09-25T06:52:35.829Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" },
+ { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" },
+ { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" },
+ { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" },
+ { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" },
+ { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
+]
+
+[[package]]
+name = "yarl"
+version = "1.25.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/75/16/e8be8e2fb175bbf41a0680381a319f1199fae256588241a2ac8677eafb49/yarl-1.25.1.tar.gz", hash = "sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7", size = 246245, upload-time = "2026-09-15T19:35:02.264Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/85/92/55fa9ee84cb8ec9930a910b5753936a926f918d8cc8965bdac571479c095/yarl-1.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:142c06c4d6a35ee3ec5da08499805e879cb3ca7c1fbfbecb0140fe72403818d6", size = 144695, upload-time = "2026-09-15T19:29:53.114Z" },
+ { url = "https://files.pythonhosted.org/packages/16/b4/9edc8e605b16b2eb5bd0c2ccc73e2b15aab583862460767fb43f91f11839/yarl-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24ce942011a61953e7d313438038f4d32ff21387b775f58a957f7a07dd55ef95", size = 104332, upload-time = "2026-09-15T19:29:55.443Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/de/4204e6646e278b1cd4a9cf73ebe8b7cfcf16693f1249b324eb273d67602a/yarl-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e23c82b63cd7652fc24d33ed6cc17099d607aa3b4fc4ddc75e95062f3d82df4", size = 104449, upload-time = "2026-09-15T19:29:57.239Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/fc/21d74789198ad68a23a209ab0d73fe5a52d39ebbd9945041e72827acdd77/yarl-1.25.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee202350cf57abf0e9502a41601841019c25d3db7ff52d980aaf31446254059", size = 116828, upload-time = "2026-09-15T19:29:59.031Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/31/90891ddb61848c067ebd1fb505ab5902a6e7d4707c2f3823f67ff37883d3/yarl-1.25.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5df89f769cc8ff94c3d7e7603386fba309d25ce5240132d26c15baa8d0e96c4c", size = 107150, upload-time = "2026-09-15T19:30:00.868Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/15/f3d18743d3688b5f01aa83034b6c00665692ab20e1d0c89a29000d4979ea/yarl-1.25.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83e9f4a25085bd4b7214701a0794ff1f50fc633ffb8bdfebf07abdd81c2db126", size = 124704, upload-time = "2026-09-15T19:30:03.128Z" },
+ { url = "https://files.pythonhosted.org/packages/99/37/718789d8004775d6a2db86b2afc72b1d156e0590b88a1b8634ddd4bdb8ee/yarl-1.25.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e636b64d24fd9c38053c5e389a1174c66361fa49dcfd220f4dd35b4abde7cb89", size = 129246, upload-time = "2026-09-15T19:30:05.014Z" },
+ { url = "https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5637ca8d0bd7fb72648a6c7934af4baaccb697657f7438c9d264fc2abb8b0b1", size = 118071, upload-time = "2026-09-15T19:30:06.977Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/e3/8d098cafb30a64df7283b5a5dd0d32d22a71b18a1b9ec071d0776ffe5c2a/yarl-1.25.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:683e362b8ba453080f7489c66f4ea794e751c35b72e7eab3575ef784c2fbc7fb", size = 116180, upload-time = "2026-09-15T19:30:08.836Z" },
+ { url = "https://files.pythonhosted.org/packages/20/9c/fbe9432478d87e00eef80242e15179639df162cb3d5d8d978fc56dcb6c51/yarl-1.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df23df54b5114a17c2d0ef192433e2e5a9f0c5178c32375e90b7cfc965f349d0", size = 116578, upload-time = "2026-09-15T19:30:10.782Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/8e/b3dfd03732236b86b0bcf06a72a37de412ff6f82e719a947c3c651d4dece/yarl-1.25.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f53dcd26694f148f738edc052b5a69234833e739f10f4c3287bdfd8ec0f7b326", size = 108886, upload-time = "2026-09-15T19:30:12.951Z" },
+ { url = "https://files.pythonhosted.org/packages/83/e1/94dacb650d4963d5f844bc3df7ba70288cb7b68ed5a4d070da71acb19b14/yarl-1.25.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b8075fe90bc08e40b8b8a1874fab42ee4c7b56af05c5886e9cc841397f916908", size = 124098, upload-time = "2026-09-15T19:30:14.963Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/3f/c93f76a218258c7bfc60dd27fd56e9102bdbc625517f4a6ee673295fe4e4/yarl-1.25.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a8c2b841478068440d8b733005d13a5ef535b9928cbc05f17182d410f32ba449", size = 115768, upload-time = "2026-09-15T19:30:16.736Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/79/4d93f13c3b05cda3c962805dec28cbc255c50239b3457808abc5633a00c2/yarl-1.25.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ca32926d7d77bcc8838425c4c95e040a3ace1cb7dfdae599013458dcda2607ca", size = 122461, upload-time = "2026-09-15T19:30:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/8e/e2ba83b3a9bfc1d3b882ad35fa8abc04a7af4d9356f2e329a23d0c8d889d/yarl-1.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:192a866877a49993949ef1975864ad8728bea28ee810f6abe1a0729c2b500426", size = 118295, upload-time = "2026-09-15T19:30:21.07Z" },
+ { url = "https://files.pythonhosted.org/packages/42/67/cb5ea1baa0c0ac60bda44ce59f601133154af2a2e67722d3517f8793855d/yarl-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3f4d48a6112712973e676bd792121fee470e432d749177162d9949d5c9460a1b", size = 102929, upload-time = "2026-09-15T19:30:23.122Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/85/9a1e98de10fc0e738d517efd2a9984c3e7db6cf35fa72b2b373988a9e9b7/yarl-1.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:48796ea00a303961507dc6c8437c4b325a6fc3f95f7c36c71b91ea9a8150963c", size = 98854, upload-time = "2026-09-15T19:30:25.102Z" },
+ { url = "https://files.pythonhosted.org/packages/83/b3/2cea721d495ca57f8f414aa4867ee263f486b274e07463158cf52f02ba7f/yarl-1.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d693bf4bf534e9ba3ae2780cfd577f5135629f7b5ac653490859d0b77864865", size = 143794, upload-time = "2026-09-15T19:30:26.946Z" },
+ { url = "https://files.pythonhosted.org/packages/55/e6/cd145cff8e5cf60b8b3c41fbecfa2a45028a8dec3fbc52bec03595ff3d3b/yarl-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ab2054c5531af2a9ba7b69b8ec91e4f884420e83a8c5e579b013084cb57e5e5d", size = 103993, upload-time = "2026-09-15T19:30:28.661Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/7c/fbb40fe2d53747c40aa36a9e2bd2178a202f942bda0b670f3306d4aefbce/yarl-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:564fdc7085d2245ab84f88882fdb1d6ac0723124bff6ded35bfb1c00f812630d", size = 104010, upload-time = "2026-09-15T19:30:31.069Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/0b/5a516f70641092283f57cf3670bdb75e7327bcc0dcb5038697e4dfbfd569/yarl-1.25.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acae6b45d1ace09b6ba3876da43b88366ef368f73b988c7f57e14231753d4420", size = 116444, upload-time = "2026-09-15T19:30:32.894Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/8a/d1a627f827b0a404ae0f5647cab0534959081cde13b0756a783469fb3b5e/yarl-1.25.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1fb2a01ba8cd9c5d2c5dc1ec35e0fc951d04b4f037541d4ac090c993ce58b3d7", size = 107565, upload-time = "2026-09-15T19:30:35.188Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/cf/c8e0aaec886840a6c4480fe44eaec7cd4319f79a563b79321473be79c56f/yarl-1.25.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e92b6bcc741b86d67606c40d3cb9c7cc8e6c737f81e31f4a94efc204456c92e3", size = 125006, upload-time = "2026-09-15T19:30:37.1Z" },
+ { url = "https://files.pythonhosted.org/packages/50/26/0cce366d54a93cdc8342965dc7663385e4db161625cc1e8b18e786753d24/yarl-1.25.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:72c34ac7ad4314c19362d5ce27626dcc8429bd30bbf8c179f4234078851f9492", size = 128717, upload-time = "2026-09-15T19:30:38.904Z" },
+ { url = "https://files.pythonhosted.org/packages/95/0c/a71501bbc1a674ff72c4d6c2b75f4d9a5af819f5244c3a7558080a8802c5/yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5add7b4ca7afeea91d52e4d4e4db3b1fe9885b71f07054560d8c4296b7441a2", size = 117728, upload-time = "2026-09-15T19:30:40.809Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/6f/c3267ca01defeed9ed9c4ff9b17bd54915432c405945233265b707475d1c/yarl-1.25.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:def538065f9e4d4cf1ae164bd59aba00dfa84f03923e0de4c3788f252d6bcd17", size = 116224, upload-time = "2026-09-15T19:30:42.818Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/69/fad57ee52d648431718ee0f1f68966a99c1352c3924688ffbdcc9d3fe51a/yarl-1.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a191bfdb30a79b98e5d175d75285f9fcb78bf0e46ba5efda042e1c72071a0de", size = 116299, upload-time = "2026-09-15T19:30:44.966Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/d4/6a8c1e29f33338687ca278cba0a8fbf6525a322c2c02a9a500ccbe041152/yarl-1.25.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:71f42c5b9a948c113bbdebfa544598321431d064ff959d32e99b1feb61d68345", size = 108625, upload-time = "2026-09-15T19:30:46.874Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/d2/3a35ae791c9cb6522c106923ff25c3230d999091e5e65511bca23bbd9914/yarl-1.25.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:72849d892954be4d09e569b8b831ac39ce58417fedc767d4308a0fe542018a40", size = 124515, upload-time = "2026-09-15T19:30:49.082Z" },
+ { url = "https://files.pythonhosted.org/packages/be/fd/2b022109a6b4af0f7dc371cf7500af380b0d4f034010243e1b0ce218dc93/yarl-1.25.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:efb01a106f971cb3752856bca2318bbdf7f01bd8823779c461586cbe5ffd5258", size = 115711, upload-time = "2026-09-15T19:30:51.208Z" },
+ { url = "https://files.pythonhosted.org/packages/18/59/f7586271136c3ddb0126bbfe661844699369b76b555fe38c4efe86870b2e/yarl-1.25.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a1daf47cd95a7c3a63456336bc5aaa8c86dd3a47d07ed3d0e76132ae4666a5a1", size = 122751, upload-time = "2026-09-15T19:30:53.535Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/0b/07f7a2d881f7e16c385b47fc1753382600847cad305dcc7fa0c25828acf8/yarl-1.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9489e6abf47ba37f332075a91444c7cfedb03e6ce99fbb2f116bfe1ce810da3b", size = 117983, upload-time = "2026-09-15T19:30:55.277Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/9d/8cdceec66a9b940700cb45931741403f045b162afd79bcb93c41cadd0972/yarl-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7306dee25b8a0e737363f347362b875094b4dc4e367311470656ae420fdbf8e", size = 102894, upload-time = "2026-09-15T19:30:57.606Z" },
+ { url = "https://files.pythonhosted.org/packages/17/f1/7ec357db1d3ad2863542d71e8fe64a126bbec17b134cd7d30951196809a5/yarl-1.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:abb1384477f5901d436b5d2e5465954de46ea6098f59163d243660b5c4461d35", size = 98609, upload-time = "2026-09-15T19:30:59.705Z" },
+ { url = "https://files.pythonhosted.org/packages/75/b3/cd32ac66ae622b854c2df0ac52106dda220d361b65a64fde7d5b3684aa3f/yarl-1.25.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4", size = 144798, upload-time = "2026-09-15T19:31:01.821Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fb/a2c52a8007c2051ba74662afb112ecf3d00346af4c25e33df9d80fd14fb8/yarl-1.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d", size = 104583, upload-time = "2026-09-15T19:31:04.05Z" },
+ { url = "https://files.pythonhosted.org/packages/be/dd/ee38aec8e09fdf957e50d4085453fbe202f56c6c3b4cf07b81cdb4f09ee9/yarl-1.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6", size = 104325, upload-time = "2026-09-15T19:31:06.338Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/b3/058dbfb1857b484c9cf9cc135659f50b85ce66e03c99e44dc2f7b6161f55/yarl-1.25.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58", size = 115358, upload-time = "2026-09-15T19:31:08.593Z" },
+ { url = "https://files.pythonhosted.org/packages/db/39/29693446cf0cf6b15a0e2f75a5d40f93c56819b05b0622196f45e95b5cc0/yarl-1.25.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad", size = 107658, upload-time = "2026-09-15T19:31:10.802Z" },
+ { url = "https://files.pythonhosted.org/packages/86/b3/3c4dd7e1af43b931fba95e0a722737f2ea94a6d199c802585282831d7abd/yarl-1.25.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40", size = 122660, upload-time = "2026-09-15T19:31:12.84Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/b5/1b60dbc3cfc9c5712b15148c206748f2bc93953ffdbe25ea75b63dfc89c9/yarl-1.25.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c", size = 126506, upload-time = "2026-09-15T19:31:15.088Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/7b/ca212cbe170ac8b96e45317ecbcf9c3c3ecf0cdec98d5b088a9c4088929b/yarl-1.25.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d", size = 117050, upload-time = "2026-09-15T19:31:17.241Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/c3/72b4938cdbe619ad71ac156182faef4908846b84dc3ca4dbb4c4e6f84014/yarl-1.25.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54", size = 114174, upload-time = "2026-09-15T19:31:19.294Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/43/268717870f9ba0cc9701a95181587f6dc8c5f387aab4aeecc83158f38a79/yarl-1.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f", size = 114944, upload-time = "2026-09-15T19:31:21.414Z" },
+ { url = "https://files.pythonhosted.org/packages/da/84/baa5bf504d51fe062c4bcaf62936da97fffb43285978d0b39984824231fd/yarl-1.25.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc", size = 108263, upload-time = "2026-09-15T19:31:23.388Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/779a2ed9e0152a601a27039bed9aead3f0b79797a67e2c44bfa444622dd8/yarl-1.25.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d", size = 122184, upload-time = "2026-09-15T19:31:25.343Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/1f/118e9e5b8f07694d63fd3222e801d7782270003f1a222aa798df3f8d5933/yarl-1.25.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1", size = 114001, upload-time = "2026-09-15T19:31:27.465Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/a4cf1cf372313734b17996d4007f9f73596e7a178b9485802e5494ecf484/yarl-1.25.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68", size = 120565, upload-time = "2026-09-15T19:31:29.47Z" },
+ { url = "https://files.pythonhosted.org/packages/05/79/ad94f93ca731bc9e44d321833ab96b82a4f9f5f63cf773f81a4aeea5ecc1/yarl-1.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707", size = 117060, upload-time = "2026-09-15T19:31:31.367Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/cc/51a7b4abf4ac593b8e7eb3794b28e5a35ae26eed8bc04787628d215af82f/yarl-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03", size = 102593, upload-time = "2026-09-15T19:31:33.329Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/21/0941a6b93a58b59a1ec75e5333bf06929b671309c43c0cd201c172d9c39f/yarl-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85", size = 97697, upload-time = "2026-09-15T19:31:35.855Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/ed/2f3129bbcc9a5c8ba12cc2b29d8060a3bab9c8043c456cfd4b5ca3188890/yarl-1.25.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:25868beca8b6765f8f7d0e11fe6dd7c66dd4b0793b9500286d20cc92352126a5", size = 143623, upload-time = "2026-09-15T19:31:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e1/f1bc3390fdca352826676b531d0712736f156919090206700421d46b2c37/yarl-1.25.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:10b2fd95332f0d716d5eee3c9fb2ce8eada19082de7fee83d32e37992fd75c26", size = 104011, upload-time = "2026-09-15T19:31:40.25Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/aa/50acc5c3e5da04172ae3c281c75405af4d2ca911e16120ab0563f4dffb66/yarl-1.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f12afda4eea8c8994a76d4df1875c765194f5fbe8a9d197929ea303caee29ec", size = 103677, upload-time = "2026-09-15T19:31:42.46Z" },
+ { url = "https://files.pythonhosted.org/packages/30/d2/7d1e0ab9f8390e1fbcede5a6dbf70d23c96ad09b8c5567f3a514d1ddb0e2/yarl-1.25.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14b79a30a93a3ce2e8832603fd0ab780ada281b0ba5110b519a634f2d7d7d1fc", size = 115392, upload-time = "2026-09-15T19:31:44.371Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e1/5ba1e3a2a22139213655e760919038e8ed7e2d4a99826d0bbddb3beb96e5/yarl-1.25.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bd6340d20ae2c7ca719b87b426e808e90743b676d05d4c26c4fb5ca71f41184", size = 107493, upload-time = "2026-09-15T19:31:46.273Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/53/780653d5e0f73831f467cf13548912e5eec97f21dc49fc8daf21da027df4/yarl-1.25.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:126a2533570c554719ca40a1288fdee1700b6bc82e7131aa69fa85252d92e651", size = 122537, upload-time = "2026-09-15T19:31:48.654Z" },
+ { url = "https://files.pythonhosted.org/packages/03/92/d54fa70236c6036271c9c9c09fd978df5cbe3ef49ef6c46e9b833476d215/yarl-1.25.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a3faadac7d812ddac258feb57b9846b60c1b437c4f4b9ad42595c6f6fe4390df", size = 126170, upload-time = "2026-09-15T19:31:50.872Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b7/a82a49bf88340b837ef6972b508a1604ae377b9e6904b46b10cf5f1cf925/yarl-1.25.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be80550d9bfe83d9b62398a37081a90434e6df2d978ec345c3d2820de6beddab", size = 117012, upload-time = "2026-09-15T19:31:53.189Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/78/5d684b411e3f3602464ee9b538db48205038f8605872985f61efb809ced0/yarl-1.25.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e07595c7d6f4db270ceede356a1bd1c07a34f1c26f958d1ed0cd7b48e0d2bba3", size = 114950, upload-time = "2026-09-15T19:31:55.694Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/11/51d82b852c64f7fad0fc7a7ff3031517204887e874c722bbca839c0b23ac/yarl-1.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eb96ed1ae6c7d072d60840c0434aef07a2df611812810807fbc54263a6053e9a", size = 115428, upload-time = "2026-09-15T19:31:57.966Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/49/9d1978049bf646b9ea918313926453c6901b71c92f097467777d47d36a88/yarl-1.25.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3feb99222553a8cbedfa52c2f59dd84c3f50d5b582c728d522caf8d72769a54b", size = 108428, upload-time = "2026-09-15T19:32:00.048Z" },
+ { url = "https://files.pythonhosted.org/packages/43/35/7b8f1ebb45d7ec3dda7d1909bf44f458de41ef91e2937f107733582a5166/yarl-1.25.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a2ed0ba415ccdf08f14bf544cb78346d0f76086707ffee24921a2c84dbf1305a", size = 121961, upload-time = "2026-09-15T19:32:02.436Z" },
+ { url = "https://files.pythonhosted.org/packages/63/d6/d8b689ab7ca26edeb85f6ff28812aac7a25376eefc1780e303a7bfbaceff/yarl-1.25.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2b49375d22299b0a834c2bca72f39aaecc270d96fb24c30424899676f487b22a", size = 114961, upload-time = "2026-09-15T19:32:04.456Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/d5/1a1798ea4dc6b7ee3260010a27907ebc697c95dae99817d817ed446d24aa/yarl-1.25.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ef74070ac553c59eb4f04258722066d6c6135b7baa03b2e9f2da65c096e96d98", size = 120036, upload-time = "2026-09-15T19:32:06.5Z" },
+ { url = "https://files.pythonhosted.org/packages/91/8d/b1b35ed7903da6669b1d367cb2c09436acd4ff508029b4f39a0c0c2058fc/yarl-1.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0a66db89ea473abeac4b70523cafd94db3772380e565f9d28af7a179b7af71fa", size = 117276, upload-time = "2026-09-15T19:32:09.401Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8f/4db01cef62caff0d7a4593ed694fb8a41a27a11158cab80d290221f13e57/yarl-1.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:1f51020b2eb8a003c84925638ec63c21a750a4bddd3a22ec8eac6a742dadf1b9", size = 101945, upload-time = "2026-09-15T19:32:11.545Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/5e/3ce00497c5c0babb74d4130c10c3828ccd215b4819d12020c42429f991ac/yarl-1.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:b10dd0557ba422715b5206b3743192135a6022acca8baec51aa127d0a75db8fe", size = 97270, upload-time = "2026-09-15T19:32:14.127Z" },
+ { url = "https://files.pythonhosted.org/packages/80/cf/54023edfab7aa773b860503db0c56e962ccab0922803ee97988c176ea090/yarl-1.25.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a9ca696eb02e5c02a8afd872ada510eba9b7fe6e68b9572c2e9a9b1941e31e2e", size = 143975, upload-time = "2026-09-15T19:32:16.416Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/a8/e6c1be0e6761d0f2d10bbf33a3e1e02b99dc83874d92945d7b461a72481e/yarl-1.25.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a5877f2255aab518ebe528289037699201d5dc5f045f2396cb30aa02db22f57f", size = 104018, upload-time = "2026-09-15T19:32:18.364Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/bb/dda344765ffd3430afe1a1c66c866a57fae67786537d4f14607df6505ac1/yarl-1.25.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7a5c3115595995779ee21f2567035793911c3802a43c74f3fbb0314929ec67ac", size = 104156, upload-time = "2026-09-15T19:32:20.459Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/5f/ed1538bcd06009fe990d6d283dd7667f639e62a81e35c6d8c6ef6c08fb3c/yarl-1.25.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77e5099b99b37f3cf79c246998ca9f7313a78054cd1809ec46bc1afad47e1c4c", size = 116025, upload-time = "2026-09-15T19:32:22.766Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/af/2185daf56b99830d3356ecfada46faaa49945de6626e842b7728088d4980/yarl-1.25.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6efaf45df6a849cef613a03a94c845647456662f85438c886bb67a9c027c8c2c", size = 106985, upload-time = "2026-09-15T19:32:24.749Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/65/bc1ae564fb4b04a30b6a8f250e787772581c57e4c3d5cf07ac3359de3103/yarl-1.25.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5f90e44653c4e0f78501ed9bb7d3fce835a8d62b7c6ed0cb16557534087e743", size = 123030, upload-time = "2026-09-15T19:32:27.084Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/3e/e2afcde10d74e53b3fa889960991efb3019beda2b1682a01de720a302056/yarl-1.25.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:632da579b2d879f6bad20f2cfa35ded1efe2f4f77f8abb26a6234a5b236acd2f", size = 126765, upload-time = "2026-09-15T19:32:29.332Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/be/415b00c0fe5a0615b062a456b26623d7ec91c2bee20faea1a14045aa0469/yarl-1.25.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30eec96e8a91bd588ce897c9543f6d5d8d34b28fbcba28a4dedf20ebeae9fe57", size = 117199, upload-time = "2026-09-15T19:32:31.49Z" },
+ { url = "https://files.pythonhosted.org/packages/97/27/3d8c63ddd3e8bcfd033748ab93876678ce59bacd66e4cb1ed851c9c5b37e/yarl-1.25.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:12b6bc4906e11f5e1a1cdcb12296e7afbd366c783cc8073403cd2fb74334e453", size = 115187, upload-time = "2026-09-15T19:32:34.137Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b7/7a81d0be1a502a26a0d4326c6f2ecb736c824f570ea1c6529f2b0b227b50/yarl-1.25.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9d6ed3d17bccce4c05343e1ca8da13bc5c02c812a4e7282ddd05e8769322d3fc", size = 116085, upload-time = "2026-09-15T19:32:36.438Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/69/39fff459916aa0fab42215dc47b759586fd80f94aa56dfc4a7c15ba6e0dc/yarl-1.25.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f38a70074041d3b7e138e452799f5174198bae5bd5ab2000917badf403908c5f", size = 107996, upload-time = "2026-09-15T19:32:38.959Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/39/80b9a55a3335590451d9ecf3eb593a8c635351f4c905ef056d7e8a8fd9e7/yarl-1.25.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca89e4e21854ed27ec753297dde84b16c9f8e53b14a4866fb44457d643c19f8", size = 122549, upload-time = "2026-09-15T19:32:41.151Z" },
+ { url = "https://files.pythonhosted.org/packages/42/7d/a179c6757818bb59372a4adafd09f7f26a3b4a0f04c3ae404b544c0b0c82/yarl-1.25.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1ab7618921a93767387a4b83776f751588f5b5ae9bb5bc96620e2e2e00bca868", size = 115107, upload-time = "2026-09-15T19:32:43.072Z" },
+ { url = "https://files.pythonhosted.org/packages/32/2b/a773ac867e4ab53a98ed98e5cefe3bae31e6f550252ca9d1de266f1a40c5/yarl-1.25.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0ae12ff2b805fa02c4dab838005caef735e39986322698c48588d3beacb65c62", size = 120666, upload-time = "2026-09-15T19:32:45.061Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/41/52be6505e85b0f76b4f85b01b5de7e06a0512201abc2c95e14e099549174/yarl-1.25.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90c30ed53546da833c700115c0064c22120d1b1560f474699fd31f22dd668233", size = 117505, upload-time = "2026-09-15T19:32:47.177Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/01/349c0386caedbbe488d519f252df54efac8a1459282d466c474bdd84a620/yarl-1.25.1-cp314-cp314-win_amd64.whl", hash = "sha256:acfa7e22aa6c6e7a5996a41d275bfa01efa7ea56ab890590280e9063e2cf5c1b", size = 103446, upload-time = "2026-09-15T19:32:49.615Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/f0/8ec63180f77912f0dc4e5a42760cb8c08d20da1d5ace3578a01b84d1f3d8/yarl-1.25.1-cp314-cp314-win_arm64.whl", hash = "sha256:8e7d98cdbb6d71e726f7d525952867096053d1f290dd4e3c50d7d313a136f414", size = 99159, upload-time = "2026-09-15T19:32:51.686Z" },
+ { url = "https://files.pythonhosted.org/packages/47/7d/92d2220d6886b70ab1ed8579533ac2af2dfac716d5d929001daff7986df9/yarl-1.25.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d21f0fa80a02d05299207eeaafef345d812ace96d5306e4ef265e1d419a615fa", size = 150071, upload-time = "2026-09-15T19:32:53.911Z" },
+ { url = "https://files.pythonhosted.org/packages/64/fc/b245e448124bcda9340df38e3553fa222b50260fca027a84095e9bd8642d/yarl-1.25.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17c9877a89fb6e2bca6f9087eb24cd7fb434653946ef5075e470d23d49b52287", size = 106780, upload-time = "2026-09-15T19:32:56.443Z" },
+ { url = "https://files.pythonhosted.org/packages/51/e2/9a6ce2e334ebf218a30335ae76fb1696459430d42f733b8cb0d7d65b84d3/yarl-1.25.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:29273edf1530e397bd07cb784db1fbe0d2590b77569f2e24679a9c0a2d763b94", size = 107361, upload-time = "2026-09-15T19:32:58.827Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/70/66e8c76b569b450d16e190f15071c916c3df70b0e33927e415ac497cf0c2/yarl-1.25.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7abffdf37af1cec6a2ad69b827aa84320db5894791bc8ed932dc93fb274b7e9", size = 114396, upload-time = "2026-09-15T19:33:02.24Z" },
+ { url = "https://files.pythonhosted.org/packages/73/23/0d82838a05c57fdc05bc8b66e8c92dcc0df15e27463a5f163142d521c682/yarl-1.25.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2239a02249d9326655419e0168a28ca9008938eaab31dc29fc875c217927a6c0", size = 104882, upload-time = "2026-09-15T19:33:04.494Z" },
+ { url = "https://files.pythonhosted.org/packages/86/d4/ea08615c4edaa6049a13a2f1128944d068d1893abda7d708d4d7ea01599a/yarl-1.25.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:664ec6a520b74a1df2810666eb67695fcb77fa663e6ea0a25aaf2e529cb24dfa", size = 119485, upload-time = "2026-09-15T19:33:06.583Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/82/0898bdce9b1ae403b308b9c733d0d24af4a3464270c2c081f457b16c3e0d/yarl-1.25.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f1c91f5a5980a937ff8e238e98e6897e1ad74a4b1e2c0d68c73b5ffbb3f5c0b", size = 122490, upload-time = "2026-09-15T19:33:08.653Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/38/97d79b81c342b78246cfedb74809e68841f3198d21653e10d3232bd9c622/yarl-1.25.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c88edaec8c349ad4c5ad4c486a3defcc4b80ceb2f074436ffa0a87caf5e76a6", size = 115336, upload-time = "2026-09-15T19:33:11.056Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/9d/2577896554cd310dc470adb6da0b7dd0b435cb63e2565204a7ac240e504c/yarl-1.25.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35dcbea443fafb3eece757ad4e514560ddeb6c34cfae1582c620d7b293d7feee", size = 111825, upload-time = "2026-09-15T19:33:13.204Z" },
+ { url = "https://files.pythonhosted.org/packages/29/6b/7ac49d8ba84a5c4bd73415a4c949d22c749cb3762579b3d50e48019a78aa/yarl-1.25.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:882569ff613758cac762a457a5d72d6e211b28d4bcfea89d1d71ea942b02eac0", size = 114655, upload-time = "2026-09-15T19:33:15.553Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/18/e5942a16723f5b72f9b1297fd5a85a54f6300cd15c0dcb5005b90cd89156/yarl-1.25.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d0f1489233a254bb3643d2f05de7d59019254d81daeca6b9162fe9edef57e0c7", size = 106395, upload-time = "2026-09-15T19:33:17.599Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2d/549fa46240781513ebc47ae7eb418df428a163a2a3d644cc9cbb3ecb7846/yarl-1.25.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f41753a76f4f63927d03a0d8ba8f5ce0f2083bec29a8cfaccc55371b1564b96b", size = 119277, upload-time = "2026-09-15T19:33:19.973Z" },
+ { url = "https://files.pythonhosted.org/packages/76/16/4763f78dcdc0b3b9fb3842b04afe72b9320857c6a69300c62a0eab03d119/yarl-1.25.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fb0eb4955adf0579001581f2f71a126e8781ba61bcd120f127b0401163c6c2d", size = 112504, upload-time = "2026-09-15T19:33:22.464Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/b4/974e3edfe0d188393ce1cb9de400111c63fe61f4eb3b772a500d84c970d1/yarl-1.25.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a1e32763e641a1566507d90a8d3b19bfc3cc04a9d4e5ae3e32189874ed4b58a3", size = 116243, upload-time = "2026-09-15T19:33:24.788Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/aa/157b940428da80c104ca09666a740e51c94963df65d5b112e06b52e4d7a8/yarl-1.25.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65b5b2066651b7432d389e9799d979c703bcc6ef44266bb8153ef54e91e4aab3", size = 115822, upload-time = "2026-09-15T19:33:26.886Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/af/19fbdce41412e1b96825544cc52cd7029d3724655d0988237972f078bd29/yarl-1.25.1-cp314-cp314t-win_amd64.whl", hash = "sha256:734f6e5400352ac4254456003d462866c684703570929cff7a7bde015d0cb371", size = 107386, upload-time = "2026-09-15T19:33:29.009Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/99/f6431c8968e89be608d74b28ae2d024521b2953f27dd44e0dece5e04f67a/yarl-1.25.1-cp314-cp314t-win_arm64.whl", hash = "sha256:287e99ff5aa4dc1c7630bfc683ded6f106d756c99dec432a2d7f197a784f51c6", size = 102094, upload-time = "2026-09-15T19:33:31.151Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/3b/4f51eab40c2eabea6c3d5b121dff4b8988dc35087732ffede12d2be8b8dd/yarl-1.25.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9b1bdaae98bc016825dd3c9d8ee1832f829b3341f9cc6ebd1a1b0a7fef7367cc", size = 143875, upload-time = "2026-09-15T19:33:33.52Z" },
+ { url = "https://files.pythonhosted.org/packages/41/05/bbd58fc063f5f299a883f810760b265ca26c8167c91cb9a494d0fe2387e1/yarl-1.25.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:e7011b8fb8c4054bf0c12e5edc6cd83778b0028e99ce59b18586ed036f92cfdc", size = 104028, upload-time = "2026-09-15T19:33:36.22Z" },
+ { url = "https://files.pythonhosted.org/packages/12/ee/2fba0aecb52e7020e189f684148783aa0b9cfa3b3bfb0b400646eef70ad4/yarl-1.25.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f074e8d4aa0a5798920ddb6de3d08b228c614ff3724c3e8bd7577f4bafea867b", size = 104041, upload-time = "2026-09-15T19:33:38.86Z" },
+ { url = "https://files.pythonhosted.org/packages/38/35/884beab53ed88c7247d1671972b5ef116f7351fe0c7e6de8c3558372cb16/yarl-1.25.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d42e7e3ca399555578b4d617e3a6ecf13371b3743a115995fa010c7bf341459", size = 116018, upload-time = "2026-09-15T19:33:44.265Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/cc/1a91b685afb55cc18608443ace95280e96e263a97565811732b6788d3269/yarl-1.25.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aa4ed3dd308548f9e707d9caaf005d2d7f8c1e7868f858dfeb47fe76e16b391d", size = 107033, upload-time = "2026-09-15T19:33:46.45Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/c1/58b379fcb1d68d907b7fcf75200c44321896509b2a6a74abbb4b19d864d2/yarl-1.25.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42a66563d8cc056ee32e6191e05097a7b2b3bc302e0bc3133daf8710eb18bd26", size = 123257, upload-time = "2026-09-15T19:33:48.631Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/2d/1fe96cf5c2aeab10095e48f38585cf5a8451fb7253234822398e52aa5336/yarl-1.25.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98d370568f393215d605304cdb77b3d5539bd192c75b623c7304c42c8d6d8273", size = 126745, upload-time = "2026-09-15T19:33:50.999Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/60/8674394ce43f4dadae573a1d6f451716438e9eab7d7fe8d643c673a32d85/yarl-1.25.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23bf5b403c879a54964e0feac7285688e04bb220074878d737d331522da0a5bf", size = 117255, upload-time = "2026-09-15T19:33:53.456Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/72/0faa30e02605d56127d42bb987dcc97da3863b7bf70b9bfbf5f739c05e30/yarl-1.25.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d45673badd08456d0340e9364eddafe1c53a9d2896424294de4d7dd71ad3ee57", size = 115166, upload-time = "2026-09-15T19:33:55.669Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/90/9a46eac564c437e128285c5c1d7bb385d268394e9209d84f6bf4a14471ef/yarl-1.25.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0136d640dfa9b0523853e411430a99f8a91eca85774c6420285a33b755bc6de3", size = 116082, upload-time = "2026-09-15T19:33:57.78Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/38/4b1a686a3758878f93d2f1ea943f5a165f3555769cd16e761cfd0efdca17/yarl-1.25.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:59ba3a6e1aa8cfe5adf4bd270fd965db21955401b7ca6f1696010c55ed4daec2", size = 108048, upload-time = "2026-09-15T19:34:00.25Z" },
+ { url = "https://files.pythonhosted.org/packages/da/14/f348eb967f31a58348612a2b93bd8a2ab664548e2b5e879cac7f592f7201/yarl-1.25.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:87796fedc3ba97ec14fab55acb48584276e6c1e4c1e89c422bda62c838e754a9", size = 122775, upload-time = "2026-09-15T19:34:02.455Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/e9/4f7b79700f88cb9e8bb66f8b54f9bce1844c013a2c39fdc47112e9334c95/yarl-1.25.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bd0912757081f89b107d6c00b2ff8a194401b0b87eadcf4481de2b865a8fd44f", size = 115096, upload-time = "2026-09-15T19:34:05.283Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/37/f9cb020331997d3eb887bd28d5410ecfd3d80bf163c23d7cec490d78dade/yarl-1.25.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:b51c159a9794633f5e0db7ecec7b2b6e3734eca1f5d17dc989ff3552a43ff78b", size = 120655, upload-time = "2026-09-15T19:34:07.382Z" },
+ { url = "https://files.pythonhosted.org/packages/12/83/52fceb22891a41f168db7ec22fd1d81e06b6a0b8d9f70921bd3e785defd0/yarl-1.25.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:319e070a01db9920fb63761843f96a104c8e2b9427266731810dc1e22595b17c", size = 117488, upload-time = "2026-09-15T19:34:09.988Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/5c/ce6c4ff1247fcbe4b33d462c23a097106d909b173fde7042bc52290466e2/yarl-1.25.1-cp315-cp315-win_amd64.whl", hash = "sha256:a2059a2d891bd156bc5184e7ab7a56e78a84dfcfdeac8c501b552533ad1c36ee", size = 103434, upload-time = "2026-09-15T19:34:12.56Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/a1/766a906b0704fb26d52b19dc22bed48a8ba0544203b70dcf44da350e8194/yarl-1.25.1-cp315-cp315-win_arm64.whl", hash = "sha256:a78b50b4f7918a3de71105d5c0b93bbc57bb8339a4d03a9dfd449f9068e76f3d", size = 99155, upload-time = "2026-09-15T19:34:15.132Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/d3/a1d09b32cb6ab14f66b44939f5b4255b8b9e747aef3974af1d5d80ccc2fd/yarl-1.25.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:b5402a340723fa7da00b5cff987ddab61276be6d11251ea71ae02bcac54890d8", size = 149284, upload-time = "2026-09-15T19:34:17.452Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/3b/fe554d879692650bca70bfbc0df124e82e4d2bb7456f698c7756f1279a96/yarl-1.25.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:eda19ea5ee88742f47a2340816e6f2d40b53bed3ab5b69794769f36af9f35bb4", size = 106399, upload-time = "2026-09-15T19:34:21.474Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/4b/e7af56177ac8d40094c82d7728224c0b8472157d50d362e5fb3b014b2bc8/yarl-1.25.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:75baa6cf9b6d1c52f3e111a130e202fd8cf0a5b3a066c3f73d615e885092e4ec", size = 106968, upload-time = "2026-09-15T19:34:23.619Z" },
+ { url = "https://files.pythonhosted.org/packages/da/4f/2df41fd738d46f23ef829ae8b4468d94bb6070038fc6dfab6165ed44fea8/yarl-1.25.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbcef5a9119ef653653132cccaf999b30a0af6f33bb0a4ba80bec30056868487", size = 114717, upload-time = "2026-09-15T19:34:25.879Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ea/002b66df53bbd1aed1c23358ff99c9bdc744fe3f4d2740e1b2fdd7192885/yarl-1.25.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7efc9f082dfed77c316edffa9deb52888e1bc6789171887cc1f68e06d65465c8", size = 105198, upload-time = "2026-09-15T19:34:28.204Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/7c/95c8bc0c8f97d71e59c94525ad60d76f5c57d3f2820f08137ca8b9f0542a/yarl-1.25.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe01645169a2112aa1d4ebc3e4c5f029c5c8f97adfc32e5d37c993b39a994d75", size = 120271, upload-time = "2026-09-15T19:34:30.5Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/7a/6fe9da56ec77927baa669fd86c39c567ce6205bab53d581082c6744c8ae7/yarl-1.25.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c557dfd5e3db046053a0bdc72261ade790ebe8e2c7a41b36b0ca1f14cb95f3", size = 123572, upload-time = "2026-09-15T19:34:32.73Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/83/35f222d17fa70a14c7c74fdf112ccf5515e0c2a87082b1f9b99f7693bf57/yarl-1.25.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ce4d6ccafb33d39bd78444612d14938ead674c25702ded2ee9c54a47735d225", size = 115228, upload-time = "2026-09-15T19:34:35.344Z" },
+ { url = "https://files.pythonhosted.org/packages/da/6f/fbaaf619423578a7d898d0f226ae47bc1906c293865c416d83c17b828b0e/yarl-1.25.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80a063f8297fc796296f00f100be520f209b23dc98f93ce8eba6ee7122598209", size = 111618, upload-time = "2026-09-15T19:34:37.656Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/78/7383278f1b3cf8e0496bd95b3281a7b09b89217b6b428db24c6b99b3deca/yarl-1.25.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7cb414a73e21a7ab58254926073f2930cb22f5b4314ea4260a687e2b3fd4dce3", size = 114839, upload-time = "2026-09-15T19:34:40.099Z" },
+ { url = "https://files.pythonhosted.org/packages/62/49/5506e5b6d29aab91bd845cc9016d88c3d3f81b81bc242b8100bdd5737825/yarl-1.25.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:85a18376073f8a39aa07be34f9fc77e2869aa72c55c441efdd2cf79a0407504d", size = 106212, upload-time = "2026-09-15T19:34:42.768Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8a/19877c193b7c5929f4b07118c18bbe390f3fadd0f59dd98c0cd12b31fa5c/yarl-1.25.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:77716e245c90f058466a05e6a465bb8600f767a8f4b18b4d40f3aff958e5f73c", size = 119985, upload-time = "2026-09-15T19:34:44.976Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/6c/0a46fbbf9ecbcbd0cc20d2193394254b9e19817f22c814aab60f99847400/yarl-1.25.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:1e80dcf1446e1b080b1932b0d103c464a04112f5bc31f0f983ad418172063cde", size = 112081, upload-time = "2026-09-15T19:34:47.45Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/30/93f5d471230c74ccd06255d0842739f86551f937f9e63a5e947853c6244a/yarl-1.25.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:bdc8d8b8c22e9e43ac68316b5e6cf083dec537f4ec213cb4aa967b583bc3fa64", size = 116995, upload-time = "2026-09-15T19:34:49.972Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/80/c386593035ee3f9c6c6af0847b5578f2830c674794a9d7701b744a3ebd42/yarl-1.25.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbf531053a0935f2e871bcd4753f90313688772ff8c017f5ea402e315a78c1f", size = 115570, upload-time = "2026-09-15T19:34:52.628Z" },
+ { url = "https://files.pythonhosted.org/packages/38/02/eef443559563ef8f2e10469387b8b1e97cb5efee95b288a56da60801f7ee/yarl-1.25.1-cp315-cp315t-win_amd64.whl", hash = "sha256:b13b88747769537f3d32e89e3a735da10c0a9e35d7322928c701b5f93d3afffd", size = 106811, upload-time = "2026-09-15T19:34:54.935Z" },
+ { url = "https://files.pythonhosted.org/packages/88/91/41e284ca2cf5211e05dae031d126a3668aea88fa759df56e7e35c6ad25ba/yarl-1.25.1-cp315-cp315t-win_arm64.whl", hash = "sha256:783dd1467083f4d3f7722ad6a313f24c173e7571372738fcb7a6e6d1ba48df25", size = 101804, upload-time = "2026-09-15T19:34:57.231Z" },
+ { url = "https://files.pythonhosted.org/packages/54/22/318c7980066769c6bcd9221ed2248294f5698811da099013098c670565ed/yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3", size = 63617, upload-time = "2026-09-15T19:34:59.616Z" },
+]
+
+[[package]]
+name = "zipp"
+version = "4.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/23/655a1802fe8041302c959774ca7c80b53bc24737ff3ef45cb50ef11bd96c/zipp-4.1.1.tar.gz", hash = "sha256:7ebb7a44c021b29fd8dbd7cce6812d0d7b5b454521f93cc71af6ccd155aaa70b", size = 27649, upload-time = "2026-10-03T17:03:03.452Z" }
wheels = [
- { url = "http://mirrors.i.h.pjlab.org.cn/repository/pypi-proxy/packages/typing-extensions/4.16.0/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8" },
+ { url = "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl", hash = "sha256:8979f52d874162f485ff2981e3891f3a3317b7a3dd43ff1e1775b9304f307a9c", size = 10582, upload-time = "2026-10-03T17:03:02.506Z" },
]